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"
27 #include <threading/mutex.h>
28 #include <tnc/tncif.h>
29 #include <tnc/tncifimv.h>
30 #include <tnc/tnccs/tnccs.h>
32 typedef struct private_tnccs_11_t private_tnccs_11_t
;
35 * Private data of a tnccs_11_t object.
37 struct private_tnccs_11_t
{
40 * Public tls_t interface.
45 * TNCC if TRUE, TNCS if FALSE
50 * Connection ID assigned to this TNCCS connection
52 TNC_ConnectionID connection_id
;
60 * TNCCS batch being constructed
65 * Mutex locking the batch in construction
70 * Flag set while processing
75 * Flag set by TNCCS-Recommendation message
80 * Flag set by IMC/IMV RequestHandshakeRetry() function
82 bool request_handshake_retry
;
85 * Set of IMV recommendations (TNC Server only)
87 recommendations_t
*recs
;
90 METHOD(tnccs_t
, send_msg
, void,
91 private_tnccs_11_t
* this, TNC_IMCID imc_id
, TNC_IMVID imv_id
,
92 TNC_BufferReference msg
,
94 TNC_MessageType msg_type
)
96 tnccs_msg_t
*tnccs_msg
;
98 tnccs_msg
= imc_imv_msg_create(msg_type
, chunk_create(msg
, msg_len
));
100 /* adding an IMC-IMV Message to TNCCS batch */
101 this->mutex
->lock(this->mutex
);
104 this->batch
= tnccs_batch_create(this->is_server
, ++this->batch_id
);
106 this->batch
->add_msg(this->batch
, tnccs_msg
);
107 this->mutex
->unlock(this->mutex
);
111 * Handle a single TNCCS message according to its type
113 static void handle_message(private_tnccs_11_t
*this, tnccs_msg_t
*msg
)
115 switch (msg
->get_type(msg
))
119 imc_imv_msg_t
*imc_imv_msg
;
120 TNC_MessageType msg_type
;
123 imc_imv_msg
= (imc_imv_msg_t
*)msg
;
124 msg_type
= imc_imv_msg
->get_msg_type(imc_imv_msg
);
125 msg_body
= imc_imv_msg
->get_msg_body(imc_imv_msg
);
127 DBG2(DBG_TNC
, "handling IMC_IMV message type 0x%08x", msg_type
);
131 charon
->imvs
->receive_message(charon
->imvs
,
132 this->connection_id
, msg_body
.ptr
, msg_body
.len
, msg_type
);
136 charon
->imcs
->receive_message(charon
->imcs
,
137 this->connection_id
, msg_body
.ptr
, msg_body
.len
,msg_type
);
141 case TNCCS_MSG_RECOMMENDATION
:
143 tnccs_recommendation_msg_t
*rec_msg
;
144 TNC_IMV_Action_Recommendation rec
;
145 TNC_ConnectionState state
= TNC_CONNECTION_STATE_ACCESS_NONE
;
147 rec_msg
= (tnccs_recommendation_msg_t
*)msg
;
148 rec
= rec_msg
->get_recommendation(rec_msg
);
151 DBG1(DBG_TNC
, "ignoring NCCS-Recommendation message from "
155 DBG1(DBG_TNC
, "TNC recommendation is '%N'",
156 TNC_IMV_Action_Recommendation_names
, rec
);
159 case TNC_IMV_ACTION_RECOMMENDATION_ALLOW
:
160 state
= TNC_CONNECTION_STATE_ACCESS_ALLOWED
;
162 case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE
:
163 state
= TNC_CONNECTION_STATE_ACCESS_ISOLATED
;
165 case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS
:
167 state
= TNC_CONNECTION_STATE_ACCESS_NONE
;
169 charon
->imcs
->notify_connection_change(charon
->imcs
,
170 this->connection_id
, state
);
171 this->delete_state
= TRUE
;
174 case TNCCS_MSG_ERROR
:
176 tnccs_error_msg_t
*err_msg
;
177 tnccs_error_type_t error_type
;
180 err_msg
= (tnccs_error_msg_t
*)msg
;
181 error_msg
= err_msg
->get_message(err_msg
, &error_type
);
182 DBG1(DBG_TNC
, "received '%N' TNCCS-Error: %s",
183 tnccs_error_type_names
, error_type
, error_msg
);
185 /* we assume that all errors are fatal */
186 this->fatal_error
= TRUE
;
189 case TNCCS_MSG_PREFERRED_LANGUAGE
:
191 tnccs_preferred_language_msg_t
*lang_msg
;
194 lang_msg
= (tnccs_preferred_language_msg_t
*)msg
;
195 lang
= lang_msg
->get_preferred_language(lang_msg
);
197 DBG2(DBG_TNC
, "setting preferred language to '%s'", lang
);
198 this->recs
->set_preferred_language(this->recs
,
199 chunk_create(lang
, strlen(lang
)));
202 case TNCCS_MSG_REASON_STRINGS
:
204 tnccs_reason_strings_msg_t
*reason_msg
;
205 chunk_t reason_string
, reason_lang
;
207 reason_msg
= (tnccs_reason_strings_msg_t
*)msg
;
208 reason_string
= reason_msg
->get_reason(reason_msg
, &reason_lang
);
209 DBG2(DBG_TNC
, "reason string is '%.*s", reason_string
.len
,
211 DBG2(DBG_TNC
, "reason language is '%.*s", reason_lang
.len
,
220 METHOD(tls_t
, process
, status_t
,
221 private_tnccs_11_t
*this, void *buf
, size_t buflen
)
224 tnccs_batch_t
*batch
;
226 enumerator_t
*enumerator
;
229 if (this->is_server
&& !this->connection_id
)
231 this->connection_id
= charon
->tnccs
->create_connection(charon
->tnccs
,
232 (tnccs_t
*)this, _send_msg
,
233 &this->request_handshake_retry
, &this->recs
);
234 if (!this->connection_id
)
238 charon
->imvs
->notify_connection_change(charon
->imvs
,
239 this->connection_id
, TNC_CONNECTION_STATE_CREATE
);
242 data
= chunk_create(buf
, buflen
);
243 DBG1(DBG_TNC
, "received TNCCS Batch (%u bytes) for Connection ID %u",
244 data
.len
, this->connection_id
);
245 DBG3(DBG_TNC
, "%.*s", data
.len
, data
.ptr
);
246 batch
= tnccs_batch_create_from_data(this->is_server
, ++this->batch_id
, data
);
247 status
= batch
->process(batch
);
249 if (status
== FAILED
)
251 this->fatal_error
= TRUE
;
252 this->mutex
->lock(this->mutex
);
255 DBG1(DBG_TNC
, "cancelling TNCCS batch");
256 this->batch
->destroy(this->batch
);
259 this->batch
= tnccs_batch_create(this->is_server
, ++this->batch_id
);
261 /* add error messages to outbound batch */
262 enumerator
= batch
->create_error_enumerator(batch
);
263 while (enumerator
->enumerate(enumerator
, &msg
))
265 this->batch
->add_msg(this->batch
, msg
->get_ref(msg
));
267 enumerator
->destroy(enumerator
);
268 this->mutex
->unlock(this->mutex
);
272 enumerator
= batch
->create_msg_enumerator(batch
);
273 while (enumerator
->enumerate(enumerator
, &msg
))
275 handle_message(this, msg
);
277 enumerator
->destroy(enumerator
);
279 /* received any TNCCS-Error messages */
280 if (this->fatal_error
)
282 DBG1(DBG_TNC
, "a fatal TNCCS-Error occurred, terminating connection");
283 batch
->destroy(batch
);
289 charon
->imvs
->batch_ending(charon
->imvs
, this->connection_id
);
293 charon
->imcs
->batch_ending(charon
->imcs
, this->connection_id
);
296 batch
->destroy(batch
);
302 * Add a recommendation message if a final recommendation is available
304 static void check_and_build_recommendation(private_tnccs_11_t
*this)
306 TNC_IMV_Action_Recommendation rec
;
307 TNC_IMV_Evaluation_Result eval
;
309 chunk_t reason
, language
;
310 enumerator_t
*enumerator
;
313 if (!this->recs
->have_recommendation(this->recs
, &rec
, &eval
))
315 charon
->imvs
->solicit_recommendation(charon
->imvs
, this->connection_id
);
317 if (this->recs
->have_recommendation(this->recs
, &rec
, &eval
))
321 this->batch
= tnccs_batch_create(this->is_server
, ++this->batch_id
);
324 msg
= tnccs_recommendation_msg_create(rec
);
325 this->batch
->add_msg(this->batch
, msg
);
327 /* currently we just send the first Reason String */
328 enumerator
= this->recs
->create_reason_enumerator(this->recs
);
329 if (enumerator
->enumerate(enumerator
, &id
, &reason
, &language
))
331 msg
= tnccs_reason_strings_msg_create(reason
, language
);
332 this->batch
->add_msg(this->batch
, msg
);
334 enumerator
->destroy(enumerator
);
336 /* we have reache the final state */
337 this->delete_state
= TRUE
;
341 METHOD(tls_t
, build
, status_t
,
342 private_tnccs_11_t
*this, void *buf
, size_t *buflen
, size_t *msglen
)
346 /* Initialize the connection */
347 if (!this->is_server
&& !this->connection_id
)
352 this->connection_id
= charon
->tnccs
->create_connection(charon
->tnccs
,
353 (tnccs_t
*)this, _send_msg
,
354 &this->request_handshake_retry
, NULL
);
355 if (!this->connection_id
)
360 /* Create TNCCS-PreferredLanguage message */
361 pref_lang
= charon
->imcs
->get_preferred_language(charon
->imcs
);
362 msg
= tnccs_preferred_language_msg_create(pref_lang
);
363 this->mutex
->lock(this->mutex
);
364 this->batch
= tnccs_batch_create(this->is_server
, ++this->batch_id
);
365 this->batch
->add_msg(this->batch
, msg
);
366 this->mutex
->unlock(this->mutex
);
368 charon
->imcs
->notify_connection_change(charon
->imcs
,
369 this->connection_id
, TNC_CONNECTION_STATE_CREATE
);
370 charon
->imcs
->notify_connection_change(charon
->imcs
,
371 this->connection_id
, TNC_CONNECTION_STATE_HANDSHAKE
);
372 charon
->imcs
->begin_handshake(charon
->imcs
, this->connection_id
);
375 /* Do not allow any asynchronous IMCs or IMVs to add additional messages */
376 this->mutex
->lock(this->mutex
);
378 if (this->is_server
&& !this->delete_state
&&
379 (!this->batch
|| this->fatal_error
))
381 check_and_build_recommendation(this);
388 this->batch
->build(this->batch
);
389 data
= this->batch
->get_encoding(this->batch
);
390 DBG1(DBG_TNC
, "sending TNCCS Batch (%d bytes) for Connection ID %u",
391 data
.len
, this->connection_id
);
392 DBG3(DBG_TNC
, "%.*s", data
.len
, data
.ptr
);
395 if (data
.len
> *buflen
)
397 DBG1(DBG_TNC
, "fragmentation of TNCCS batch not supported yet");
403 memcpy(buf
, data
.ptr
, *buflen
);
404 this->batch
->destroy(this->batch
);
406 status
= ALREADY_DONE
;
410 DBG1(DBG_TNC
, "no TNCCS Batch to send");
411 status
= INVALID_STATE
;
413 this->mutex
->unlock(this->mutex
);
418 METHOD(tls_t
, is_server
, bool,
419 private_tnccs_11_t
*this)
421 return this->is_server
;
424 METHOD(tls_t
, get_purpose
, tls_purpose_t
,
425 private_tnccs_11_t
*this)
427 return TLS_PURPOSE_EAP_TNC
;
430 METHOD(tls_t
, is_complete
, bool,
431 private_tnccs_11_t
*this)
433 TNC_IMV_Action_Recommendation rec
;
434 TNC_IMV_Evaluation_Result eval
;
436 if (this->recs
&& this->recs
->have_recommendation(this->recs
, &rec
, &eval
))
438 DBG2(DBG_TNC
, "Final recommendation is '%N' and evaluation is '%N'",
439 TNC_IMV_Action_Recommendation_names
, rec
,
440 TNC_IMV_Evaluation_Result_names
, eval
);
442 return charon
->imvs
->enforce_recommendation(charon
->imvs
, rec
);
450 METHOD(tls_t
, get_eap_msk
, chunk_t
,
451 private_tnccs_11_t
*this)
456 METHOD(tls_t
, destroy
, void,
457 private_tnccs_11_t
*this)
461 charon
->imvs
->notify_connection_change(charon
->imvs
,
462 this->connection_id
, TNC_CONNECTION_STATE_DELETE
);
466 charon
->imcs
->notify_connection_change(charon
->imcs
,
467 this->connection_id
, TNC_CONNECTION_STATE_DELETE
);
469 charon
->tnccs
->remove_connection(charon
->tnccs
, this->connection_id
);
470 this->mutex
->destroy(this->mutex
);
471 DESTROY_IF(this->batch
);
478 tls_t
*tnccs_11_create(bool is_server
)
480 private_tnccs_11_t
*this;
486 .is_server
= _is_server
,
487 .get_purpose
= _get_purpose
,
488 .is_complete
= _is_complete
,
489 .get_eap_msk
= _get_eap_msk
,
492 .is_server
= is_server
,
493 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
496 return &this->public;