2 * Copyright (C) 2010 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
17 #include "batch/tnccs_batch.h"
18 #include "messages/tnccs_msg.h"
19 #include "messages/imc_imv_msg.h"
20 #include "messages/tnccs_error_msg.h"
21 #include "messages/tnccs_preferred_language_msg.h"
22 #include "messages/tnccs_reason_strings_msg.h"
23 #include "messages/tnccs_recommendation_msg.h"
25 #include <tncif_names.h>
26 #include <tncif_pa_subtypes.h>
29 #include <tnc/imc/imc_manager.h>
30 #include <tnc/imv/imv_manager.h>
31 #include <tnc/tnccs/tnccs.h>
32 #include <tnc/tnccs/tnccs_manager.h>
35 #include <threading/mutex.h>
37 typedef struct private_tnccs_11_t private_tnccs_11_t
;
40 * Private data of a tnccs_11_t object.
42 struct private_tnccs_11_t
{
45 * Public tls_t interface.
50 * TNCC if TRUE, TNCS if FALSE
55 * Connection ID assigned to this TNCCS connection
57 TNC_ConnectionID connection_id
;
65 * TNCCS batch being constructed
70 * Mutex locking the batch in construction
75 * Flag set while processing
80 * Flag set by TNCCS-Recommendation message
85 * SendMessage() by IMC/IMV only allowed if flag is set
90 * Flag set by IMC/IMV RequestHandshakeRetry() function
92 bool request_handshake_retry
;
95 * Set of IMV recommendations (TNC Server only)
97 recommendations_t
*recs
;
101 METHOD(tnccs_t
, send_msg
, TNC_Result
,
102 private_tnccs_11_t
* this, TNC_IMCID imc_id
, TNC_IMVID imv_id
,
103 TNC_BufferReference msg
,
105 TNC_MessageType msg_type
)
107 tnccs_msg_t
*tnccs_msg
;
108 u_int32_t vendor_id
, subtype
;
109 enum_name_t
*pa_subtype_names
;
113 DBG1(DBG_TNC
, "%s %u not allowed to call SendMessage()",
114 this->is_server ?
"IMV" : "IMC",
115 this->is_server ? imv_id
: imc_id
);
116 return TNC_RESULT_ILLEGAL_OPERATION
;
118 vendor_id
= msg_type
>> 8;
119 subtype
= msg_type
& 0xff;
120 pa_subtype_names
= get_pa_subtype_names(vendor_id
);
121 if (pa_subtype_names
)
123 DBG2(DBG_TNC
, "creating IMC-IMV message type '%N/%N' 0x%06x/0x%02x",
124 pen_names
, vendor_id
, pa_subtype_names
, subtype
, vendor_id
, subtype
);
128 DBG2(DBG_TNC
, "creating IMC-IMV message type '%N' 0x%06x/0x%02x",
129 pen_names
, vendor_id
, vendor_id
, subtype
);
131 tnccs_msg
= imc_imv_msg_create(msg_type
, chunk_create(msg
, msg_len
));
133 /* adding an IMC-IMV Message to TNCCS batch */
134 this->mutex
->lock(this->mutex
);
137 this->batch
= tnccs_batch_create(this->is_server
, ++this->batch_id
);
139 this->batch
->add_msg(this->batch
, tnccs_msg
);
140 this->mutex
->unlock(this->mutex
);
141 return TNC_RESULT_SUCCESS
;
145 * Handle a single TNCCS message according to its type
147 static void handle_message(private_tnccs_11_t
*this, tnccs_msg_t
*msg
)
149 switch (msg
->get_type(msg
))
153 imc_imv_msg_t
*imc_imv_msg
;
154 TNC_MessageType msg_type
;
156 u_int32_t vendor_id
, subtype
;
157 enum_name_t
*pa_subtype_names
;
159 imc_imv_msg
= (imc_imv_msg_t
*)msg
;
160 msg_type
= imc_imv_msg
->get_msg_type(imc_imv_msg
);
161 msg_body
= imc_imv_msg
->get_msg_body(imc_imv_msg
);
162 vendor_id
= msg_type
>> 8;
163 subtype
= msg_type
& 0xff;
165 pa_subtype_names
= get_pa_subtype_names(vendor_id
);
166 if (pa_subtype_names
)
168 DBG2(DBG_TNC
, "handling IMC-IMV message type '%N/%N' 0x%06x/0x%02x",
169 pen_names
, vendor_id
, pa_subtype_names
, subtype
,
174 DBG2(DBG_TNC
, "handling IMC-IMV message type '%N' 0x%06x/0x%02x",
175 pen_names
, vendor_id
, vendor_id
, subtype
);
178 this->send_msg
= TRUE
;
181 tnc
->imvs
->receive_message(tnc
->imvs
,
182 this->connection_id
, msg_body
.ptr
, msg_body
.len
, msg_type
);
186 tnc
->imcs
->receive_message(tnc
->imcs
,
187 this->connection_id
, msg_body
.ptr
, msg_body
.len
,msg_type
);
189 this->send_msg
= FALSE
;
192 case TNCCS_MSG_RECOMMENDATION
:
194 tnccs_recommendation_msg_t
*rec_msg
;
195 TNC_IMV_Action_Recommendation rec
;
196 TNC_ConnectionState state
= TNC_CONNECTION_STATE_ACCESS_NONE
;
198 rec_msg
= (tnccs_recommendation_msg_t
*)msg
;
199 rec
= rec_msg
->get_recommendation(rec_msg
);
202 DBG1(DBG_TNC
, "ignoring NCCS-Recommendation message from "
206 DBG1(DBG_TNC
, "TNC recommendation is '%N'",
207 TNC_IMV_Action_Recommendation_names
, rec
);
210 case TNC_IMV_ACTION_RECOMMENDATION_ALLOW
:
211 state
= TNC_CONNECTION_STATE_ACCESS_ALLOWED
;
213 case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE
:
214 state
= TNC_CONNECTION_STATE_ACCESS_ISOLATED
;
216 case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS
:
218 state
= TNC_CONNECTION_STATE_ACCESS_NONE
;
220 tnc
->imcs
->notify_connection_change(tnc
->imcs
, this->connection_id
,
222 this->delete_state
= TRUE
;
225 case TNCCS_MSG_ERROR
:
227 tnccs_error_msg_t
*err_msg
;
228 tnccs_error_type_t error_type
;
231 err_msg
= (tnccs_error_msg_t
*)msg
;
232 error_msg
= err_msg
->get_message(err_msg
, &error_type
);
233 DBG1(DBG_TNC
, "received '%N' TNCCS-Error: %s",
234 tnccs_error_type_names
, error_type
, error_msg
);
236 /* we assume that all errors are fatal */
237 this->fatal_error
= TRUE
;
240 case TNCCS_MSG_PREFERRED_LANGUAGE
:
242 tnccs_preferred_language_msg_t
*lang_msg
;
245 lang_msg
= (tnccs_preferred_language_msg_t
*)msg
;
246 lang
= lang_msg
->get_preferred_language(lang_msg
);
248 DBG2(DBG_TNC
, "setting preferred language to '%s'", lang
);
249 this->recs
->set_preferred_language(this->recs
,
250 chunk_create(lang
, strlen(lang
)));
253 case TNCCS_MSG_REASON_STRINGS
:
255 tnccs_reason_strings_msg_t
*reason_msg
;
256 chunk_t reason_string
, reason_lang
;
258 reason_msg
= (tnccs_reason_strings_msg_t
*)msg
;
259 reason_string
= reason_msg
->get_reason(reason_msg
, &reason_lang
);
260 DBG2(DBG_TNC
, "reason string is '%.*s'", reason_string
.len
,
262 DBG2(DBG_TNC
, "reason language is '%.*s'", reason_lang
.len
,
271 METHOD(tls_t
, process
, status_t
,
272 private_tnccs_11_t
*this, void *buf
, size_t buflen
)
275 tnccs_batch_t
*batch
;
277 enumerator_t
*enumerator
;
280 if (this->is_server
&& !this->connection_id
)
282 this->connection_id
= tnc
->tnccs
->create_connection(tnc
->tnccs
,
283 (tnccs_t
*)this, _send_msg
,
284 &this->request_handshake_retry
, &this->recs
);
285 if (!this->connection_id
)
289 tnc
->imvs
->notify_connection_change(tnc
->imvs
, this->connection_id
,
290 TNC_CONNECTION_STATE_CREATE
);
291 tnc
->imvs
->notify_connection_change(tnc
->imvs
, this->connection_id
,
292 TNC_CONNECTION_STATE_HANDSHAKE
);
295 data
= chunk_create(buf
, buflen
);
296 DBG1(DBG_TNC
, "received TNCCS Batch (%u bytes) for Connection ID %u",
297 data
.len
, this->connection_id
);
298 DBG3(DBG_TNC
, "%.*s", data
.len
, data
.ptr
);
299 batch
= tnccs_batch_create_from_data(this->is_server
, ++this->batch_id
, data
);
300 status
= batch
->process(batch
);
302 if (status
== FAILED
)
304 this->fatal_error
= TRUE
;
305 this->mutex
->lock(this->mutex
);
308 DBG1(DBG_TNC
, "cancelling TNCCS batch");
309 this->batch
->destroy(this->batch
);
312 this->batch
= tnccs_batch_create(this->is_server
, ++this->batch_id
);
314 /* add error messages to outbound batch */
315 enumerator
= batch
->create_error_enumerator(batch
);
316 while (enumerator
->enumerate(enumerator
, &msg
))
318 this->batch
->add_msg(this->batch
, msg
->get_ref(msg
));
320 enumerator
->destroy(enumerator
);
321 this->mutex
->unlock(this->mutex
);
325 enumerator
= batch
->create_msg_enumerator(batch
);
326 while (enumerator
->enumerate(enumerator
, &msg
))
328 handle_message(this, msg
);
330 enumerator
->destroy(enumerator
);
332 /* received any TNCCS-Error messages */
333 if (this->fatal_error
)
335 DBG1(DBG_TNC
, "a fatal TNCCS-Error occurred, terminating connection");
336 batch
->destroy(batch
);
340 this->send_msg
= TRUE
;
343 tnc
->imvs
->batch_ending(tnc
->imvs
, this->connection_id
);
347 tnc
->imcs
->batch_ending(tnc
->imcs
, this->connection_id
);
349 this->send_msg
= FALSE
;
351 batch
->destroy(batch
);
357 * Add a recommendation message if a final recommendation is available
359 static void check_and_build_recommendation(private_tnccs_11_t
*this)
361 TNC_IMV_Action_Recommendation rec
;
362 TNC_IMV_Evaluation_Result eval
;
364 chunk_t reason
, language
;
365 enumerator_t
*enumerator
;
368 if (!this->recs
->have_recommendation(this->recs
, &rec
, &eval
))
370 tnc
->imvs
->solicit_recommendation(tnc
->imvs
, this->connection_id
);
372 if (this->recs
->have_recommendation(this->recs
, &rec
, &eval
))
376 this->batch
= tnccs_batch_create(this->is_server
, ++this->batch_id
);
379 msg
= tnccs_recommendation_msg_create(rec
);
380 this->batch
->add_msg(this->batch
, msg
);
382 /* currently we just send the first Reason String */
383 enumerator
= this->recs
->create_reason_enumerator(this->recs
);
384 if (enumerator
->enumerate(enumerator
, &id
, &reason
, &language
))
386 msg
= tnccs_reason_strings_msg_create(reason
, language
);
387 this->batch
->add_msg(this->batch
, msg
);
389 enumerator
->destroy(enumerator
);
390 this->recs
->clear_reasons(this->recs
);
392 /* we have reache the final state */
393 this->delete_state
= TRUE
;
397 METHOD(tls_t
, build
, status_t
,
398 private_tnccs_11_t
*this, void *buf
, size_t *buflen
, size_t *msglen
)
402 /* Initialize the connection */
403 if (!this->is_server
&& !this->connection_id
)
408 this->connection_id
= tnc
->tnccs
->create_connection(tnc
->tnccs
,
409 (tnccs_t
*)this, _send_msg
,
410 &this->request_handshake_retry
, NULL
);
411 if (!this->connection_id
)
416 /* Create TNCCS-PreferredLanguage message */
417 pref_lang
= tnc
->imcs
->get_preferred_language(tnc
->imcs
);
418 msg
= tnccs_preferred_language_msg_create(pref_lang
);
419 this->mutex
->lock(this->mutex
);
420 this->batch
= tnccs_batch_create(this->is_server
, ++this->batch_id
);
421 this->batch
->add_msg(this->batch
, msg
);
422 this->mutex
->unlock(this->mutex
);
424 tnc
->imcs
->notify_connection_change(tnc
->imcs
, this->connection_id
,
425 TNC_CONNECTION_STATE_CREATE
);
426 tnc
->imcs
->notify_connection_change(tnc
->imcs
, this->connection_id
,
427 TNC_CONNECTION_STATE_HANDSHAKE
);
428 this->send_msg
= TRUE
;
429 tnc
->imcs
->begin_handshake(tnc
->imcs
, this->connection_id
);
430 this->send_msg
= FALSE
;
433 /* Do not allow any asynchronous IMCs or IMVs to add additional messages */
434 this->mutex
->lock(this->mutex
);
436 if (this->recs
&& !this->delete_state
&&
437 (!this->batch
|| this->fatal_error
))
439 check_and_build_recommendation(this);
446 this->batch
->build(this->batch
);
447 data
= this->batch
->get_encoding(this->batch
);
448 DBG1(DBG_TNC
, "sending TNCCS Batch (%d bytes) for Connection ID %u",
449 data
.len
, this->connection_id
);
450 DBG3(DBG_TNC
, "%.*s", data
.len
, data
.ptr
);
453 if (data
.len
> *buflen
)
455 DBG1(DBG_TNC
, "fragmentation of TNCCS batch not supported yet");
461 memcpy(buf
, data
.ptr
, *buflen
);
462 this->batch
->destroy(this->batch
);
464 status
= ALREADY_DONE
;
468 DBG1(DBG_TNC
, "no TNCCS Batch to send");
469 status
= INVALID_STATE
;
471 this->mutex
->unlock(this->mutex
);
476 METHOD(tls_t
, is_server
, bool,
477 private_tnccs_11_t
*this)
479 return this->is_server
;
482 METHOD(tls_t
, get_purpose
, tls_purpose_t
,
483 private_tnccs_11_t
*this)
485 return TLS_PURPOSE_EAP_TNC
;
488 METHOD(tls_t
, is_complete
, bool,
489 private_tnccs_11_t
*this)
491 TNC_IMV_Action_Recommendation rec
;
492 TNC_IMV_Evaluation_Result eval
;
494 if (this->recs
&& this->recs
->have_recommendation(this->recs
, &rec
, &eval
))
496 return tnc
->imvs
->enforce_recommendation(tnc
->imvs
, rec
, eval
);
504 METHOD(tls_t
, get_eap_msk
, chunk_t
,
505 private_tnccs_11_t
*this)
510 METHOD(tls_t
, destroy
, void,
511 private_tnccs_11_t
*this)
513 tnc
->tnccs
->remove_connection(tnc
->tnccs
, this->connection_id
,
515 this->mutex
->destroy(this->mutex
);
516 DESTROY_IF(this->batch
);
523 tls_t
*tnccs_11_create(bool is_server
)
525 private_tnccs_11_t
*this;
531 .is_server
= _is_server
,
532 .get_purpose
= _get_purpose
,
533 .is_complete
= _is_complete
,
534 .get_eap_msk
= _get_eap_msk
,
537 .is_server
= is_server
,
538 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
541 return &this->public;