2 * Copyright (C) 2015 Andreas Steffen
4 * Copyright (C) secunet Security Networks AG
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>.
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
17 #include "tnccs_20_server.h"
18 #include "messages/pb_tnc_msg.h"
19 #include "messages/ietf/pb_pa_msg.h"
20 #include "messages/ietf/pb_error_msg.h"
21 #include "messages/ietf/pb_assessment_result_msg.h"
22 #include "messages/ietf/pb_access_recommendation_msg.h"
23 #include "messages/ietf/pb_remediation_parameters_msg.h"
24 #include "messages/ietf/pb_reason_string_msg.h"
25 #include "messages/ietf/pb_language_preference_msg.h"
26 #include "messages/ita/pb_mutual_capability_msg.h"
27 #include "messages/ita/pb_noskip_test_msg.h"
28 #include "messages/tcg/pb_pdp_referral_msg.h"
29 #include "state_machine/pb_tnc_state_machine.h"
31 #include <tncif_names.h>
32 #include <tncif_pa_subtypes.h>
35 #include <tnc/tnccs/tnccs_manager.h>
36 #include <tnc/imv/imv_manager.h>
38 #include <threading/mutex.h>
39 #include <utils/debug.h>
40 #include <collections/linked_list.h>
43 typedef struct private_tnccs_20_server_t private_tnccs_20_server_t
;
46 * Private data of a tnccs_20_server_t object.
48 struct private_tnccs_20_server_t
{
51 * Public tnccs_20_server_t interface.
53 tnccs_20_server_t
public;
56 * PB-TNC State Machine
58 pb_tnc_state_machine_t
*state_machine
;
61 * Connection ID assigned to this TNCCS connection
63 TNC_ConnectionID connection_id
;
66 * PB-TNC messages to be sent
68 linked_list_t
*messages
;
71 * Type of PB-TNC batch being constructed
73 pb_tnc_batch_type_t batch_type
;
76 * Maximum PB-TNC batch size
81 * Mutex locking the batch in construction
86 * Flag set while processing
91 * Flag set by IMC/IMV RequestHandshakeRetry() function
93 bool request_handshake_retry
;
96 * Flag set after sending SRETRY batch
101 * SendMessage() by IMV only allowed if flag is set
106 * Set of IMV recommendations
108 recommendations_t
*recs
;
111 * TNC IF-T transport protocol for EAP methods
116 * Mutual PB-TNC protocol enabled
121 * Mutual Capability message sent
123 bool sent_mutual_capability
;
128 * The following two functions are shared with the tnccs_20_server class
130 extern void tnccs_20_handle_ietf_error_msg(pb_tnc_msg_t
*msg
,
132 extern bool tnccs_20_handle_ita_mutual_capability_msg(pb_tnc_msg_t
*msg
);
135 * If the batch type changes then delete all accumulated PB-TNC messages
137 static void change_batch_type(private_tnccs_20_server_t
*this,
138 pb_tnc_batch_type_t batch_type
)
142 if (batch_type
!= this->batch_type
)
144 if (this->batch_type
!= PB_BATCH_NONE
)
146 DBG1(DBG_TNC
, "canceling PB-TNC %N batch",
147 pb_tnc_batch_type_names
, this->batch_type
);
149 while (this->messages
->remove_last(this->messages
,
150 (void**)&msg
) == SUCCESS
)
155 this->batch_type
= batch_type
;
160 * Handle a single PB-TNC IETF standard message according to its type
162 static void handle_ietf_message(private_tnccs_20_server_t
*this, pb_tnc_msg_t
*msg
)
164 pen_type_t msg_type
= msg
->get_type(msg
);
166 switch (msg_type
.type
)
168 case PB_MSG_EXPERIMENTAL
:
174 pen_type_t msg_subtype
;
175 uint16_t imc_id
, imv_id
;
178 enum_name_t
*pa_subtype_names
;
180 pa_msg
= (pb_pa_msg_t
*)msg
;
181 msg_subtype
= pa_msg
->get_subtype(pa_msg
);
182 msg_body
= pa_msg
->get_body(pa_msg
);
183 imc_id
= pa_msg
->get_collector_id(pa_msg
);
184 imv_id
= pa_msg
->get_validator_id(pa_msg
);
185 excl
= pa_msg
->get_exclusive_flag(pa_msg
);
187 pa_subtype_names
= get_pa_subtype_names(msg_subtype
.vendor_id
);
188 if (pa_subtype_names
)
190 DBG2(DBG_TNC
, "handling PB-PA message type '%N/%N' 0x%06x/0x%08x",
191 pen_names
, msg_subtype
.vendor_id
, pa_subtype_names
,
192 msg_subtype
.type
, msg_subtype
.vendor_id
, msg_subtype
.type
);
196 DBG2(DBG_TNC
, "handling PB-PA message type '%N' 0x%06x/0x%08x",
197 pen_names
, msg_subtype
.vendor_id
, msg_subtype
.vendor_id
,
200 this->send_msg
= TRUE
;
201 tnc
->imvs
->receive_message(tnc
->imvs
, this->connection_id
,
202 excl
, msg_body
.ptr
, msg_body
.len
,
203 msg_subtype
.vendor_id
,
204 msg_subtype
.type
, imc_id
, imv_id
);
205 this->send_msg
= FALSE
;
209 tnccs_20_handle_ietf_error_msg(msg
, &this->fatal_error
);
211 case PB_MSG_LANGUAGE_PREFERENCE
:
213 pb_language_preference_msg_t
*lang_msg
;
216 lang_msg
= (pb_language_preference_msg_t
*)msg
;
217 lang
= lang_msg
->get_language_preference(lang_msg
);
218 DBG2(DBG_TNC
, "setting language preference to '%.*s'",
219 (int)lang
.len
, lang
.ptr
);
220 this->recs
->set_preferred_language(this->recs
, lang
);
229 * Handle a single PB-TNC ITA standard message according to its type
231 static void handle_ita_message(private_tnccs_20_server_t
*this, pb_tnc_msg_t
*msg
)
233 pen_type_t msg_type
= msg
->get_type(msg
);
235 switch (msg_type
.type
)
237 case PB_ITA_MSG_MUTUAL_CAPABILITY
:
238 this->mutual
= tnccs_20_handle_ita_mutual_capability_msg(msg
);
240 /* Respond with PB-TNC Mutual Capability message if necessary */
241 if (this->mutual
&& !this->sent_mutual_capability
)
243 msg
= pb_mutual_capability_msg_create(PB_MUTUAL_HALF_DUPLEX
);
244 this->mutex
->lock(this->mutex
);
245 this->messages
->insert_last(this->messages
, msg
);
246 this->mutex
->unlock(this->mutex
);
247 this->sent_mutual_capability
= TRUE
;
256 * Handle a single PB-TNC message according to its type
258 static void handle_message(private_tnccs_20_server_t
*this, pb_tnc_msg_t
*msg
)
260 pen_type_t msg_type
= msg
->get_type(msg
);
262 switch (msg_type
.vendor_id
)
265 handle_ietf_message(this, msg
);
268 handle_ita_message(this, msg
);
276 * Build an SRETRY batch
278 static void build_retry_batch(private_tnccs_20_server_t
*this)
280 if (this->batch_type
== PB_BATCH_SRETRY
)
282 /* retry batch has already been selected */
285 change_batch_type(this, PB_BATCH_SRETRY
);
288 METHOD(tnccs_20_handler_t
, process
, status_t
,
289 private_tnccs_20_server_t
*this, pb_tnc_batch_t
*batch
)
291 pb_tnc_batch_type_t batch_type
;
294 batch_type
= batch
->get_type(batch
);
296 DBG1(DBG_TNC
, "processing PB-TNC %N batch for Connection ID %d",
297 pb_tnc_batch_type_names
, batch_type
, this->connection_id
);
298 status
= batch
->process(batch
, this->state_machine
);
300 if (status
!= FAILED
)
302 enumerator_t
*enumerator
;
306 if (batch_type
== PB_BATCH_CRETRY
||
307 (batch_type
== PB_BATCH_CDATA
&& this->retry_handshake
))
309 this->recs
->clear_recommendation(this->recs
);
310 tnc
->imvs
->notify_connection_change(tnc
->imvs
,
311 this->connection_id
, TNC_CONNECTION_STATE_HANDSHAKE
);
312 this->retry_handshake
= FALSE
;
315 enumerator
= batch
->create_msg_enumerator(batch
);
316 while (enumerator
->enumerate(enumerator
, &msg
))
318 handle_message(this, msg
);
321 enumerator
->destroy(enumerator
);
323 /* received a CLOSE batch from PB-TNC client */
324 if (batch_type
== PB_BATCH_CLOSE
)
326 return empty
? SUCCESS
: FAILED
;
329 this->send_msg
= TRUE
;
330 tnc
->imvs
->batch_ending(tnc
->imvs
, this->connection_id
);
331 this->send_msg
= FALSE
;
337 this->fatal_error
= TRUE
;
338 status
= VERIFY_ERROR
;
352 * Build a RESULT batch if a final recommendation is available
354 static void check_and_build_recommendation(private_tnccs_20_server_t
*this)
356 TNC_IMV_Action_Recommendation rec
;
357 TNC_IMV_Evaluation_Result eval
;
358 TNC_ConnectionState state
;
360 chunk_t reason
, language
;
361 enumerator_t
*enumerator
;
363 pb_access_recommendation_code_t pb_rec
;
365 if (!this->recs
->have_recommendation(this->recs
, &rec
, &eval
))
367 tnc
->imvs
->solicit_recommendation(tnc
->imvs
, this->connection_id
);
369 if (this->recs
->have_recommendation(this->recs
, &rec
, &eval
))
371 this->batch_type
= PB_BATCH_RESULT
;
373 msg
= pb_assessment_result_msg_create(eval
);
374 this->messages
->insert_last(this->messages
, msg
);
377 * Map IMV Action Recommendation codes to PB Access Recommendation codes
378 * and communicate Access Recommendation to IMVs
382 case TNC_IMV_ACTION_RECOMMENDATION_ALLOW
:
383 state
= TNC_CONNECTION_STATE_ACCESS_ALLOWED
;
384 pb_rec
= PB_REC_ACCESS_ALLOWED
;
386 case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE
:
387 state
= TNC_CONNECTION_STATE_ACCESS_ISOLATED
;
388 pb_rec
= PB_REC_QUARANTINED
;
390 case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS
:
391 case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
:
393 state
= TNC_CONNECTION_STATE_ACCESS_NONE
;
394 pb_rec
= PB_REC_ACCESS_DENIED
;
396 tnc
->imvs
->notify_connection_change(tnc
->imvs
, this->connection_id
,
399 msg
= pb_access_recommendation_msg_create(pb_rec
);
400 this->messages
->insert_last(this->messages
, msg
);
402 enumerator
= this->recs
->create_reason_enumerator(this->recs
);
403 while (enumerator
->enumerate(enumerator
, &id
, &reason
, &language
))
405 msg
= pb_reason_string_msg_create(reason
, language
);
406 this->messages
->insert_last(this->messages
, msg
);
408 enumerator
->destroy(enumerator
);
412 METHOD(tnccs_20_handler_t
, build
, status_t
,
413 private_tnccs_20_server_t
*this, void *buf
, size_t *buflen
, size_t *msglen
)
416 pb_tnc_state_t state
;
418 state
= this->state_machine
->get_state(this->state_machine
);
420 if (this->fatal_error
&& state
== PB_STATE_END
)
422 DBG1(DBG_TNC
, "a fatal PB-TNC error occurred, terminating connection");
426 /* Do not allow any asynchronous IMVs to add additional messages */
427 this->mutex
->lock(this->mutex
);
429 if (this->request_handshake_retry
)
431 if (state
== PB_STATE_DECIDED
)
433 build_retry_batch(this);
435 /* Handshake will be retried with next incoming CDATA batch */
436 this->retry_handshake
= TRUE
;
439 /* Reset the flag for the next handshake retry request */
440 this->request_handshake_retry
= FALSE
;
443 if (state
== PB_STATE_SERVER_WORKING
&&
444 this->recs
->have_recommendation(this->recs
, NULL
, NULL
))
446 check_and_build_recommendation(this);
449 if (this->batch_type
== PB_BATCH_NONE
)
451 if (state
== PB_STATE_SERVER_WORKING
)
453 if (this->state_machine
->get_empty_cdata(this->state_machine
))
455 check_and_build_recommendation(this);
459 DBG2(DBG_TNC
, "no recommendation available yet, "
460 "sending empty PB-TNC SDATA batch");
461 this->batch_type
= PB_BATCH_SDATA
;
466 if (this->batch_type
!= PB_BATCH_NONE
)
468 pb_tnc_batch_t
*batch
;
472 enumerator_t
*enumerator
;
474 if (this->state_machine
->send_batch(this->state_machine
, this->batch_type
))
476 batch
= pb_tnc_batch_create(TRUE
, this->batch_type
,
477 min(this->max_batch_len
, *buflen
));
479 enumerator
= this->messages
->create_enumerator(this->messages
);
480 while (enumerator
->enumerate(enumerator
, &msg
))
482 if (batch
->add_msg(batch
, msg
))
484 this->messages
->remove_at(this->messages
, enumerator
);
491 enumerator
->destroy(enumerator
);
494 data
= batch
->get_encoding(batch
);
495 DBG1(DBG_TNC
, "sending PB-TNC %N batch (%d bytes) for Connection ID %u",
496 pb_tnc_batch_type_names
, this->batch_type
, data
.len
,
497 this->connection_id
);
498 DBG3(DBG_TNC
, "%B", &data
);
502 memcpy(buf
, data
.ptr
, *buflen
);
503 batch
->destroy(batch
);
505 msg_count
= this->messages
->get_count(this->messages
);
508 DBG2(DBG_TNC
, "queued %d PB-TNC message%s for next %N batch",
509 msg_count
, (msg_count
== 1) ? "" : "s",
510 pb_tnc_batch_type_names
, this->batch_type
);
514 this->batch_type
= PB_BATCH_NONE
;
517 status
= ALREADY_DONE
;
521 change_batch_type(this, PB_BATCH_NONE
);
522 status
= INVALID_STATE
;
527 DBG1(DBG_TNC
, "no PB-TNC batch to send");
528 status
= INVALID_STATE
;
530 this->mutex
->unlock(this->mutex
);
535 METHOD(tnccs_20_handler_t
, begin_handshake
, void,
536 private_tnccs_20_server_t
*this, bool mutual
)
539 identification_t
*pdp_server
;
542 tnc
->imvs
->notify_connection_change(tnc
->imvs
, this->connection_id
,
543 TNC_CONNECTION_STATE_HANDSHAKE
);
545 /* Send a PB-TNC TCG PDP Referral message if PDP is known */
546 pdp_server
= (identification_t
*)lib
->get(lib
, "pt-tls-server");
547 pdp_port
= (uint16_t*)lib
->get(lib
, "pt-tls-port");
549 if (this->eap_transport
&& pdp_server
&& pdp_port
)
551 msg
= pb_pdp_referral_msg_create_from_fqdn(
552 pdp_server
->get_encoding(pdp_server
), *pdp_port
);
553 this->mutex
->lock(this->mutex
);
554 this->messages
->insert_last(this->messages
, msg
);
555 this->mutex
->unlock(this->mutex
);
558 /* Send a PB-Noskip-Test message for testing purposes */
559 if (lib
->settings
->get_bool(lib
->settings
,
560 "%s.plugins.tnccs-20.tests.pb_tnc_noskip", FALSE
, lib
->ns
))
562 msg
= pb_noskip_test_msg_create();
563 this->mutex
->lock(this->mutex
);
564 this->messages
->insert_last(this->messages
, msg
);
565 this->mutex
->unlock(this->mutex
);
569 METHOD(tnccs_20_handler_t
, get_send_flag
, bool,
570 private_tnccs_20_server_t
*this)
572 return this->send_msg
;
575 METHOD(tnccs_20_handler_t
, get_mutual
, bool,
576 private_tnccs_20_server_t
*this)
581 METHOD(tnccs_20_handler_t
, get_state
, pb_tnc_state_t
,
582 private_tnccs_20_server_t
*this)
584 return this->state_machine
->get_state(this->state_machine
);
587 METHOD(tnccs_20_handler_t
, add_msg
, void,
588 private_tnccs_20_server_t
*this, pb_tnc_msg_t
*msg
)
590 /* adding PA message to SDATA batch only */
591 this->mutex
->lock(this->mutex
);
592 if (this->batch_type
== PB_BATCH_NONE
)
594 this->batch_type
= PB_BATCH_SDATA
;
596 if (this->batch_type
== PB_BATCH_SDATA
)
598 this->messages
->insert_last(this->messages
, msg
);
604 this->mutex
->unlock(this->mutex
);
607 METHOD(tnccs_20_handler_t
, handle_errors
, void,
608 private_tnccs_20_server_t
*this, pb_tnc_batch_t
*batch
,
609 bool fatal_header_error
)
612 enumerator_t
*enumerator
;
614 if (fatal_header_error
|| this->fatal_error
)
616 this->mutex
->lock(this->mutex
);
617 change_batch_type(this, PB_BATCH_CLOSE
);
618 this->mutex
->unlock(this->mutex
);
621 enumerator
= batch
->create_error_enumerator(batch
);
622 while (enumerator
->enumerate(enumerator
, &msg
))
624 this->mutex
->lock(this->mutex
);
625 this->messages
->insert_last(this->messages
, msg
->get_ref(msg
));
626 this->mutex
->unlock(this->mutex
);
628 enumerator
->destroy(enumerator
);
631 METHOD(tnccs_20_handler_t
, destroy
, void,
632 private_tnccs_20_server_t
*this)
634 if (this->connection_id
)
636 tnc
->tnccs
->remove_connection(tnc
->tnccs
, this->connection_id
, TRUE
);
638 this->state_machine
->destroy(this->state_machine
);
639 this->mutex
->destroy(this->mutex
);
640 this->messages
->destroy_offset(this->messages
,
641 offsetof(pb_tnc_msg_t
, destroy
));
645 METHOD(tnccs_20_server_t
, have_recommendation
, bool,
646 private_tnccs_20_server_t
*this, TNC_IMV_Action_Recommendation
*rec
,
647 TNC_IMV_Evaluation_Result
*eval
)
649 return this->recs
->have_recommendation(this->recs
, rec
, eval
);
655 tnccs_20_handler_t
* tnccs_20_server_create(tnccs_t
*tnccs
,
656 tnccs_send_message_t send_msg
,
657 size_t max_batch_len
,
661 private_tnccs_20_server_t
*this;
668 .begin_handshake
= _begin_handshake
,
669 .get_send_flag
= _get_send_flag
,
670 .get_mutual
= _get_mutual
,
671 .get_state
= _get_state
,
673 .handle_errors
= _handle_errors
,
676 .have_recommendation
= _have_recommendation
,
678 .state_machine
= pb_tnc_state_machine_create(TRUE
),
679 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
680 .messages
= linked_list_create(),
681 .batch_type
= PB_BATCH_SDATA
,
682 .max_batch_len
= max_batch_len
,
683 .eap_transport
= eap_transport
,
686 this->connection_id
= tnc
->tnccs
->create_connection(tnc
->tnccs
,
687 TNCCS_2_0
, tnccs
, send_msg
,
688 &this->request_handshake_retry
,
689 max_msg_len
, &this->recs
);
690 if (!this->connection_id
)
695 tnc
->imvs
->notify_connection_change(tnc
->imvs
, this->connection_id
,
696 TNC_CONNECTION_STATE_CREATE
);
698 return &this->public.handler
;