Keep a copy of the tnccs instance for PT-TLS handover
[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/ietf/pb_pa_msg.h"
21 #include "messages/ietf/pb_error_msg.h"
22 #include "messages/ietf/pb_assessment_result_msg.h"
23 #include "messages/ietf/pb_access_recommendation_msg.h"
24 #include "messages/ietf/pb_remediation_parameters_msg.h"
25 #include "messages/ietf/pb_reason_string_msg.h"
26 #include "messages/ietf/pb_language_preference_msg.h"
27 #include "messages/tcg/pb_pdp_referral_msg.h"
28 #include "state_machine/pb_tnc_state_machine.h"
29
30 #include <tncif_names.h>
31 #include <tncif_pa_subtypes.h>
32
33 #include <tnc/tnc.h>
34 #include <tnc/tnccs/tnccs_manager.h>
35 #include <tnc/imc/imc_manager.h>
36 #include <tnc/imv/imv_manager.h>
37
38 #include <threading/mutex.h>
39 #include <utils/debug.h>
40 #include <collections/linked_list.h>
41 #include <pen/pen.h>
42
43 typedef struct private_tnccs_20_t private_tnccs_20_t;
44
45 /**
46 * Private data of a tnccs_20_t object.
47 */
48 struct private_tnccs_20_t {
49
50 /**
51 * Public tnccs_t interface.
52 */
53 tnccs_t public;
54
55 /**
56 * TNCC if TRUE, TNCS if FALSE
57 */
58 bool is_server;
59
60 /**
61 * Server identity
62 */
63 identification_t *server;
64
65 /**
66 * Client identity
67 */
68 identification_t *peer;
69
70 /**
71 * Underlying TNC IF-T transport protocol
72 */
73 tnc_ift_type_t transport;
74
75 /**
76 * Type of TNC client authentication
77 */
78 u_int32_t auth_type;
79
80 /**
81 * PB-TNC State Machine
82 */
83 pb_tnc_state_machine_t *state_machine;
84
85 /**
86 * Connection ID assigned to this TNCCS connection
87 */
88 TNC_ConnectionID connection_id;
89
90 /**
91 * PB-TNC messages to be sent
92 */
93 linked_list_t *messages;
94
95 /**
96 * Type of PB-TNC batch being constructed
97 */
98 pb_tnc_batch_type_t batch_type;
99
100 /**
101 * Maximum PB-TNC batch size
102 */
103 size_t max_batch_len;
104
105 /**
106 * Maximum PA-TNC message size
107 */
108 size_t max_msg_len;
109
110 /**
111 * Mutex locking the batch in construction
112 */
113 mutex_t *mutex;
114
115 /**
116 * Flag set while processing
117 */
118 bool fatal_error;
119
120 /**
121 * Flag set by IMC/IMV RequestHandshakeRetry() function
122 */
123 bool request_handshake_retry;
124
125 /**
126 * SendMessage() by IMC/IMV only allowed if flag is set
127 */
128 bool send_msg;
129
130 /**
131 * Set of IMV recommendations (TNC Server only)
132 */
133 recommendations_t *recs;
134
135 /**
136 * Callback function to communicate recommendation (TNC Server only)
137 */
138 tnccs_cb_t callback;
139
140 /**
141 * Data to pass to callback function (TNC Server only)
142 */
143 void *cb_data;
144
145 /**
146 * PDP server FQDN
147 */
148 chunk_t pdp_server;
149
150 /**
151 * PDP server port
152 */
153 u_int16_t pdp_port;
154
155 /**
156 * reference count
157 */
158 refcount_t ref;
159
160 };
161
162 /**
163 * If the batch type changes then delete all accumulated PB-TNC messages
164 */
165 void change_batch_type(private_tnccs_20_t *this, pb_tnc_batch_type_t batch_type)
166 {
167 pb_tnc_msg_t *msg;
168
169 if (batch_type != this->batch_type)
170 {
171 if (this->batch_type != PB_BATCH_NONE)
172 {
173 DBG1(DBG_TNC, "cancelling PB-TNC %N batch",
174 pb_tnc_batch_type_names, this->batch_type);
175
176 while (this->messages->remove_last(this->messages,
177 (void**)&msg) == SUCCESS)
178 {
179 msg->destroy(msg);
180 }
181 }
182 this->batch_type = batch_type;
183 }
184 }
185
186 METHOD(tnccs_t, send_msg, TNC_Result,
187 private_tnccs_20_t* this, TNC_IMCID imc_id, TNC_IMVID imv_id,
188 TNC_UInt32 msg_flags,
189 TNC_BufferReference msg,
190 TNC_UInt32 msg_len,
191 TNC_VendorID msg_vid,
192 TNC_MessageSubtype msg_subtype)
193 {
194 pb_tnc_msg_t *pb_tnc_msg;
195 pb_tnc_batch_type_t batch_type;
196 enum_name_t *pa_subtype_names;
197 bool excl;
198
199 if (!this->send_msg)
200 {
201 DBG1(DBG_TNC, "%s %u not allowed to call SendMessage()",
202 this->is_server ? "IMV" : "IMC",
203 this->is_server ? imv_id : imc_id);
204 return TNC_RESULT_ILLEGAL_OPERATION;
205 }
206 excl = (msg_flags & TNC_MESSAGE_FLAGS_EXCLUSIVE) != 0;
207
208 pb_tnc_msg = pb_pa_msg_create(msg_vid, msg_subtype, imc_id, imv_id,
209 excl, chunk_create(msg, msg_len));
210
211 pa_subtype_names = get_pa_subtype_names(msg_vid);
212 if (pa_subtype_names)
213 {
214 DBG2(DBG_TNC, "creating PB-PA message type '%N/%N' 0x%06x/0x%08x",
215 pen_names, msg_vid, pa_subtype_names, msg_subtype,
216 msg_vid, msg_subtype);
217 }
218 else
219 {
220 DBG2(DBG_TNC, "creating PB-PA message type '%N' 0x%06x/0x%08x",
221 pen_names, msg_vid, msg_vid, msg_subtype);
222 }
223
224 /* adding PA message to SDATA or CDATA batch only */
225 batch_type = this->is_server ? PB_BATCH_SDATA : PB_BATCH_CDATA;
226 this->mutex->lock(this->mutex);
227 if (this->batch_type == PB_BATCH_NONE)
228 {
229 this->batch_type = batch_type;
230 }
231 if (this->batch_type == batch_type)
232 {
233 this->messages->insert_last(this->messages, pb_tnc_msg);
234 }
235 else
236 {
237 pb_tnc_msg->destroy(pb_tnc_msg);
238 }
239 this->mutex->unlock(this->mutex);
240 return TNC_RESULT_SUCCESS;
241 }
242
243 /**
244 * Handle a single PB-TNC IETF standard message according to its type
245 */
246 static void handle_ietf_message(private_tnccs_20_t *this, pb_tnc_msg_t *msg)
247 {
248 pen_type_t msg_type = msg->get_type(msg);
249
250 switch (msg_type.type)
251 {
252 case PB_MSG_EXPERIMENTAL:
253 /* nothing to do */
254 break;
255 case PB_MSG_PA:
256 {
257 pb_pa_msg_t *pa_msg;
258 pen_type_t msg_subtype;
259 u_int16_t imc_id, imv_id;
260 chunk_t msg_body;
261 bool excl;
262 enum_name_t *pa_subtype_names;
263
264 pa_msg = (pb_pa_msg_t*)msg;
265 msg_subtype = pa_msg->get_subtype(pa_msg);
266 msg_body = pa_msg->get_body(pa_msg);
267 imc_id = pa_msg->get_collector_id(pa_msg);
268 imv_id = pa_msg->get_validator_id(pa_msg);
269 excl = pa_msg->get_exclusive_flag(pa_msg);
270
271 pa_subtype_names = get_pa_subtype_names(msg_subtype.vendor_id);
272 if (pa_subtype_names)
273 {
274 DBG2(DBG_TNC, "handling PB-PA message type '%N/%N' 0x%06x/0x%08x",
275 pen_names, msg_subtype.vendor_id, pa_subtype_names,
276 msg_subtype.type, msg_subtype.vendor_id, msg_subtype.type);
277 }
278 else
279 {
280 DBG2(DBG_TNC, "handling PB-PA message type '%N' 0x%06x/0x%08x",
281 pen_names, msg_subtype.vendor_id, msg_subtype.vendor_id,
282 msg_subtype.type);
283 }
284
285 this->send_msg = TRUE;
286 if (this->is_server)
287 {
288 tnc->imvs->receive_message(tnc->imvs, this->connection_id,
289 excl, msg_body.ptr, msg_body.len,
290 msg_subtype.vendor_id,
291 msg_subtype.type, imc_id, imv_id);
292 }
293 else
294 {
295 tnc->imcs->receive_message(tnc->imcs, this->connection_id,
296 excl, msg_body.ptr, msg_body.len,
297 msg_subtype.vendor_id,
298 msg_subtype.type, imv_id, imc_id);
299 }
300 this->send_msg = FALSE;
301 break;
302 }
303 case PB_MSG_ASSESSMENT_RESULT:
304 {
305 pb_assessment_result_msg_t *assess_msg;
306 u_int32_t result;
307
308 assess_msg = (pb_assessment_result_msg_t*)msg;
309 result = assess_msg->get_assessment_result(assess_msg);
310 DBG1(DBG_TNC, "PB-TNC assessment result is '%N'",
311 TNC_IMV_Evaluation_Result_names, result);
312 break;
313 }
314 case PB_MSG_ACCESS_RECOMMENDATION:
315 {
316 pb_access_recommendation_msg_t *rec_msg;
317 pb_access_recommendation_code_t rec;
318 TNC_ConnectionState state = TNC_CONNECTION_STATE_ACCESS_NONE;
319
320 rec_msg = (pb_access_recommendation_msg_t*)msg;
321 rec = rec_msg->get_access_recommendation(rec_msg);
322 DBG1(DBG_TNC, "PB-TNC access recommendation is '%N'",
323 pb_access_recommendation_code_names, rec);
324 switch (rec)
325 {
326 case PB_REC_ACCESS_ALLOWED:
327 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
328 break;
329 case PB_REC_ACCESS_DENIED:
330 state = TNC_CONNECTION_STATE_ACCESS_NONE;
331 break;
332 case PB_REC_QUARANTINED:
333 state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
334 }
335 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
336 state);
337 break;
338 }
339 case PB_MSG_REMEDIATION_PARAMETERS:
340 {
341 pb_remediation_parameters_msg_t *rem_msg;
342 pen_type_t parameters_type;
343 chunk_t parameters, string, lang_code;
344
345 rem_msg = (pb_remediation_parameters_msg_t*)msg;
346 parameters_type = rem_msg->get_parameters_type(rem_msg);
347 parameters = rem_msg->get_parameters(rem_msg);
348
349 if (parameters_type.vendor_id == PEN_IETF)
350 {
351 switch (parameters_type.type)
352 {
353 case PB_REMEDIATION_URI:
354 DBG1(DBG_TNC, "remediation uri: %.*s",
355 parameters.len, parameters.ptr);
356 break;
357 case PB_REMEDIATION_STRING:
358 string = rem_msg->get_string(rem_msg, &lang_code);
359 DBG1(DBG_TNC, "remediation string: [%.*s]\n%.*s",
360 lang_code.len, lang_code.ptr,
361 string.len, string.ptr);
362 break;
363 default:
364 DBG1(DBG_TNC, "remediation parameters: %B", &parameters);
365 }
366 }
367 else
368 {
369 DBG1(DBG_TNC, "remediation parameters: %B", &parameters);
370 }
371 break;
372 }
373 case PB_MSG_ERROR:
374 {
375 pb_error_msg_t *err_msg;
376 bool fatal;
377 u_int32_t vendor_id;
378 u_int16_t error_code;
379
380 err_msg = (pb_error_msg_t*)msg;
381 fatal = err_msg->get_fatal_flag(err_msg);
382 vendor_id = err_msg->get_vendor_id(err_msg);
383 error_code = err_msg->get_error_code(err_msg);
384
385 if (fatal)
386 {
387 this->fatal_error = TRUE;
388 }
389
390 if (vendor_id == PEN_IETF)
391 {
392 switch (error_code)
393 {
394 case PB_ERROR_INVALID_PARAMETER:
395 case PB_ERROR_UNSUPPORTED_MANDATORY_MSG:
396 DBG1(DBG_TNC, "received %s PB-TNC error '%N' "
397 "(offset %u bytes)",
398 fatal ? "fatal" : "non-fatal",
399 pb_tnc_error_code_names, error_code,
400 err_msg->get_offset(err_msg));
401 break;
402 case PB_ERROR_VERSION_NOT_SUPPORTED:
403 DBG1(DBG_TNC, "received %s PB-TNC error '%N' "
404 "caused by bad version 0x%02x",
405 fatal ? "fatal" : "non-fatal",
406 pb_tnc_error_code_names, error_code,
407 err_msg->get_bad_version(err_msg));
408 break;
409 case PB_ERROR_UNEXPECTED_BATCH_TYPE:
410 case PB_ERROR_LOCAL_ERROR:
411 default:
412 DBG1(DBG_TNC, "received %s PB-TNC error '%N'",
413 fatal ? "fatal" : "non-fatal",
414 pb_tnc_error_code_names, error_code);
415 break;
416 }
417 }
418 else
419 {
420 DBG1(DBG_TNC, "received %s PB-TNC error (%u) "
421 "with Vendor ID 0x%06x",
422 fatal ? "fatal" : "non-fatal",
423 error_code, vendor_id);
424 }
425 break;
426 }
427 case PB_MSG_LANGUAGE_PREFERENCE:
428 {
429 pb_language_preference_msg_t *lang_msg;
430 chunk_t lang;
431
432 lang_msg = (pb_language_preference_msg_t*)msg;
433 lang = lang_msg->get_language_preference(lang_msg);
434
435 if (this->recs)
436 {
437 DBG2(DBG_TNC, "setting language preference to '%.*s'",
438 (int)lang.len, lang.ptr);
439 this->recs->set_preferred_language(this->recs, lang);
440 }
441 break;
442 }
443 case PB_MSG_REASON_STRING:
444 {
445 pb_reason_string_msg_t *reason_msg;
446 chunk_t reason_string, language_code;
447
448 reason_msg = (pb_reason_string_msg_t*)msg;
449 reason_string = reason_msg->get_reason_string(reason_msg);
450 language_code = reason_msg->get_language_code(reason_msg);
451 DBG1(DBG_TNC, "reason string is '%.*s' [%.*s]",
452 (int)reason_string.len, reason_string.ptr,
453 (int)language_code.len, language_code.ptr);
454 break;
455 }
456 default:
457 break;
458 }
459 }
460
461 /**
462 * Handle a single PB-TNC TCG standard message according to its type
463 */
464 static void handle_tcg_message(private_tnccs_20_t *this, pb_tnc_msg_t *msg)
465 {
466 pen_type_t msg_type = msg->get_type(msg);
467
468 switch (msg_type.type)
469 {
470 case PB_TCG_MSG_PDP_REFERRAL:
471 {
472 pb_pdp_referral_msg_t *pdp_msg;
473 pen_type_t pdp_id_type;
474 u_int8_t pdp_protocol;
475
476 pdp_msg = (pb_pdp_referral_msg_t*)msg;
477 pdp_id_type = pdp_msg->get_identifier_type(pdp_msg);
478
479 if (pdp_id_type.vendor_id == PEN_TCG &&
480 pdp_id_type.type == PB_PDP_ID_FQDN)
481 {
482 this->pdp_server = chunk_clone(pdp_msg->get_fqdn(pdp_msg,
483 &pdp_protocol, &this->pdp_port));
484 if (pdp_protocol != 0)
485 {
486 DBG1(DBG_TNC, "unsupported PDP transport protocol");
487 break;
488 }
489 DBG1(DBG_TNC, "PDP server '%.*s' is listening on port %u",
490 this->pdp_server.len, this->pdp_server.ptr,
491 this->pdp_port);
492 }
493 break;
494 }
495 default:
496 break;
497 }
498 }
499
500 /**
501 * Handle a single PB-TNC message according to its type
502 */
503 static void handle_message(private_tnccs_20_t *this, pb_tnc_msg_t *msg)
504 {
505 pen_type_t msg_type = msg->get_type(msg);
506
507 switch (msg_type.vendor_id)
508 {
509 case PEN_IETF:
510 handle_ietf_message(this, msg);
511 break;
512 case PEN_TCG:
513 handle_tcg_message(this, msg);
514 break;
515 default:
516 break;
517 }
518 }
519
520 /**
521 * Build a CRETRY or SRETRY batch
522 */
523 static void build_retry_batch(private_tnccs_20_t *this)
524 {
525 pb_tnc_batch_type_t batch_retry_type;
526
527 batch_retry_type = this->is_server ? PB_BATCH_SRETRY : PB_BATCH_CRETRY;
528 if (this->batch_type == batch_retry_type)
529 {
530 /* retry batch has already been selected */
531 return;
532 }
533
534 change_batch_type(this, batch_retry_type);
535
536 if (this->is_server)
537 {
538 this->recs->clear_recommendation(this->recs);
539 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
540 TNC_CONNECTION_STATE_HANDSHAKE);
541 }
542 }
543
544 METHOD(tls_t, process, status_t,
545 private_tnccs_20_t *this, void *buf, size_t buflen)
546 {
547 chunk_t data;
548 pb_tnc_batch_t *batch;
549 pb_tnc_msg_t *msg;
550 enumerator_t *enumerator;
551 identification_t *pdp_server;
552 u_int16_t *pdp_port;
553 status_t status;
554
555 if (this->is_server && !this->connection_id)
556 {
557 this->connection_id = tnc->tnccs->create_connection(tnc->tnccs,
558 TNCCS_2_0, (tnccs_t*)this, _send_msg,
559 &this->request_handshake_retry,
560 this->max_msg_len, &this->recs);
561 if (!this->connection_id)
562 {
563 return FAILED;
564 }
565 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
566 TNC_CONNECTION_STATE_CREATE);
567 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
568 TNC_CONNECTION_STATE_HANDSHAKE);
569
570 /* Send a PB-TNC TCG PDP Referral message if PDP is known */
571 pdp_server = (identification_t*)lib->get(lib, "pt-tls-server");
572 pdp_port = (u_int16_t*)lib->get(lib, "pt-tls-port");
573
574 if ((this->transport == TNC_IFT_EAP_1_1 ||
575 this->transport == TNC_IFT_EAP_2_0) && pdp_server && pdp_port)
576 {
577 msg = pb_pdp_referral_msg_create_from_fqdn(
578 pdp_server->get_encoding(pdp_server), *pdp_port);
579 this->messages->insert_last(this->messages, msg);
580 }
581
582 }
583
584 data = chunk_create(buf, buflen);
585 DBG1(DBG_TNC, "received TNCCS batch (%u bytes) for Connection ID %u",
586 data.len, this->connection_id);
587 DBG3(DBG_TNC, "%B", &data);
588 batch = pb_tnc_batch_create_from_data(this->is_server, data);
589 status = batch->process(batch, this->state_machine);
590
591 if (status != FAILED)
592 {
593 enumerator_t *enumerator;
594 pb_tnc_msg_t *msg;
595 pb_tnc_batch_type_t batch_type;
596 bool empty = TRUE;
597
598 batch_type = batch->get_type(batch);
599
600 if (batch_type == PB_BATCH_CRETRY)
601 {
602 /* Send an SRETRY batch in response */
603 this->mutex->lock(this->mutex);
604 build_retry_batch(this);
605 this->mutex->unlock(this->mutex);
606 }
607 else if (batch_type == PB_BATCH_SRETRY)
608 {
609 /* Restart the measurements */
610 tnc->imcs->notify_connection_change(tnc->imcs,
611 this->connection_id, TNC_CONNECTION_STATE_HANDSHAKE);
612 this->send_msg = TRUE;
613 tnc->imcs->begin_handshake(tnc->imcs, this->connection_id);
614 this->send_msg = FALSE;
615 }
616
617 enumerator = batch->create_msg_enumerator(batch);
618 while (enumerator->enumerate(enumerator, &msg))
619 {
620 handle_message(this, msg);
621 empty = FALSE;
622 }
623 enumerator->destroy(enumerator);
624
625 /* received an empty CLOSE batch from PB-TNC client */
626 if (this->is_server && batch_type == PB_BATCH_CLOSE && empty)
627 {
628 batch->destroy(batch);
629 if (this->fatal_error)
630 {
631 DBG1(DBG_TNC, "a fatal PB-TNC error occurred, "
632 "terminating connection");
633 return FAILED;
634 }
635 else
636 {
637 return SUCCESS;
638 }
639 }
640
641 this->send_msg = TRUE;
642 if (this->is_server)
643 {
644 tnc->imvs->batch_ending(tnc->imvs, this->connection_id);
645 }
646 else
647 {
648 tnc->imcs->batch_ending(tnc->imcs, this->connection_id);
649 }
650 this->send_msg = FALSE;
651 }
652
653 switch (status)
654 {
655 case FAILED:
656 this->fatal_error = TRUE;
657 this->mutex->lock(this->mutex);
658 change_batch_type(this, PB_BATCH_CLOSE);
659 this->mutex->unlock(this->mutex);
660 /* fall through to add error messages to outbound batch */
661 case VERIFY_ERROR:
662 enumerator = batch->create_error_enumerator(batch);
663 while (enumerator->enumerate(enumerator, &msg))
664 {
665 this->mutex->lock(this->mutex);
666 this->messages->insert_last(this->messages, msg->get_ref(msg));
667 this->mutex->unlock(this->mutex);
668 }
669 enumerator->destroy(enumerator);
670 break;
671 case SUCCESS:
672 default:
673 break;
674 }
675 batch->destroy(batch);
676
677 return NEED_MORE;
678 }
679
680 /**
681 * Build a RESULT batch if a final recommendation is available
682 */
683 static void check_and_build_recommendation(private_tnccs_20_t *this)
684 {
685 TNC_IMV_Action_Recommendation rec;
686 TNC_IMV_Evaluation_Result eval;
687 TNC_ConnectionState state;
688 TNC_IMVID id;
689 chunk_t reason, language;
690 enumerator_t *enumerator;
691 pb_tnc_msg_t *msg;
692 pb_access_recommendation_code_t pb_rec;
693
694 if (!this->recs->have_recommendation(this->recs, &rec, &eval))
695 {
696 tnc->imvs->solicit_recommendation(tnc->imvs, this->connection_id);
697 }
698 if (this->recs->have_recommendation(this->recs, &rec, &eval))
699 {
700 this->batch_type = PB_BATCH_RESULT;
701
702 msg = pb_assessment_result_msg_create(eval);
703 this->messages->insert_last(this->messages, msg);
704
705 /**
706 * Map IMV Action Recommendation codes to PB Access Recommendation codes
707 * and communicate Access Recommendation to IMVs
708 */
709 switch (rec)
710 {
711 case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
712 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
713 pb_rec = PB_REC_ACCESS_ALLOWED;
714 break;
715 case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
716 state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
717 pb_rec = PB_REC_QUARANTINED;
718 break;
719 case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
720 case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
721 default:
722 state = TNC_CONNECTION_STATE_ACCESS_NONE;
723 pb_rec = PB_REC_ACCESS_DENIED;
724 }
725 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
726 state);
727
728 msg = pb_access_recommendation_msg_create(pb_rec);
729 this->messages->insert_last(this->messages, msg);
730
731 enumerator = this->recs->create_reason_enumerator(this->recs);
732 while (enumerator->enumerate(enumerator, &id, &reason, &language))
733 {
734 msg = pb_reason_string_msg_create(reason, language);
735 this->messages->insert_last(this->messages, msg);
736 }
737 enumerator->destroy(enumerator);
738 }
739 }
740
741 METHOD(tls_t, build, status_t,
742 private_tnccs_20_t *this, void *buf, size_t *buflen, size_t *msglen)
743 {
744 status_t status;
745 pb_tnc_state_t state;
746
747 /* Initialize the connection */
748 if (!this->is_server && !this->connection_id)
749 {
750 pb_tnc_msg_t *msg;
751 char *pref_lang;
752
753 this->connection_id = tnc->tnccs->create_connection(tnc->tnccs,
754 TNCCS_2_0, (tnccs_t*)this, _send_msg,
755 &this->request_handshake_retry,
756 this->max_msg_len, NULL);
757 if (!this->connection_id)
758 {
759 return FAILED;
760 }
761
762 /* Create PB-TNC Language Preference message */
763 pref_lang = tnc->imcs->get_preferred_language(tnc->imcs);
764 msg = pb_language_preference_msg_create(chunk_create(pref_lang,
765 strlen(pref_lang)));
766 this->mutex->lock(this->mutex);
767 this->batch_type = PB_BATCH_CDATA;
768 this->messages->insert_last(this->messages, msg);
769 this->mutex->unlock(this->mutex);
770
771 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
772 TNC_CONNECTION_STATE_CREATE);
773 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
774 TNC_CONNECTION_STATE_HANDSHAKE);
775 this->send_msg = TRUE;
776 tnc->imcs->begin_handshake(tnc->imcs, this->connection_id);
777 this->send_msg = FALSE;
778 }
779
780 state = this->state_machine->get_state(this->state_machine);
781
782 if (this->fatal_error && state == PB_STATE_END)
783 {
784 DBG1(DBG_TNC, "a fatal PB-TNC error occurred, terminating connection");
785 return FAILED;
786 }
787
788 /* Do not allow any asynchronous IMCs or IMVs to add additional messages */
789 this->mutex->lock(this->mutex);
790
791 if (this->request_handshake_retry)
792 {
793 if (state != PB_STATE_INIT)
794 {
795 build_retry_batch(this);
796 }
797
798 /* Reset the flag for the next handshake retry request */
799 this->request_handshake_retry = FALSE;
800 }
801
802 if (this->is_server && state == PB_STATE_SERVER_WORKING &&
803 this->recs->have_recommendation(this->recs, NULL, NULL))
804 {
805 check_and_build_recommendation(this);
806 }
807
808 if (this->batch_type == PB_BATCH_NONE)
809 {
810 if (this->is_server)
811 {
812 if (state == PB_STATE_SERVER_WORKING)
813 {
814 if (this->state_machine->get_empty_cdata(this->state_machine))
815 {
816 check_and_build_recommendation(this);
817 }
818 else
819 {
820 DBG2(DBG_TNC, "no recommendation available yet, "
821 "sending empty PB-TNC SDATA batch");
822 this->batch_type = PB_BATCH_SDATA;
823 }
824 }
825 }
826 else
827 {
828 switch (state)
829 {
830 case PB_STATE_CLIENT_WORKING:
831 DBG2(DBG_TNC, "no client data to send, "
832 "sending empty PB-TNC CDATA batch");
833 this->batch_type = PB_BATCH_CDATA;
834 break;
835 case PB_STATE_DECIDED:
836 /**
837 * In the DECIDED state and if no CRETRY is under way,
838 * a PB-TNC client replies with an empty CLOSE batch.
839 */
840 this->batch_type = PB_BATCH_CLOSE;
841 break;
842 default:
843 break;
844 }
845 }
846 }
847
848 if (this->batch_type != PB_BATCH_NONE)
849 {
850 pb_tnc_batch_t *batch;
851 pb_tnc_msg_t *msg;
852 chunk_t data;
853 int msg_count;
854 enumerator_t *enumerator;
855
856 if (this->state_machine->send_batch(this->state_machine, this->batch_type))
857 {
858 batch = pb_tnc_batch_create(this->is_server, this->batch_type,
859 min(this->max_batch_len, *buflen));
860
861 enumerator = this->messages->create_enumerator(this->messages);
862 while (enumerator->enumerate(enumerator, &msg))
863 {
864 if (batch->add_msg(batch, msg))
865 {
866 this->messages->remove_at(this->messages, enumerator);
867 }
868 else
869 {
870 break;
871 }
872 }
873 enumerator->destroy(enumerator);
874
875 batch->build(batch);
876 data = batch->get_encoding(batch);
877 DBG1(DBG_TNC, "sending PB-TNC %N batch (%d bytes) for Connection ID %u",
878 pb_tnc_batch_type_names, this->batch_type, data.len,
879 this->connection_id);
880 DBG3(DBG_TNC, "%B", &data);
881
882 *buflen = data.len;
883 *msglen = 0;
884 memcpy(buf, data.ptr, *buflen);
885 batch->destroy(batch);
886
887 msg_count = this->messages->get_count(this->messages);
888 if (msg_count)
889 {
890 DBG2(DBG_TNC, "queued %d PB-TNC message%s for next %N batch",
891 msg_count, (msg_count == 1) ? "" : "s",
892 pb_tnc_batch_type_names, this->batch_type);
893 }
894 else
895 {
896 this->batch_type = PB_BATCH_NONE;
897 }
898
899 status = ALREADY_DONE;
900 }
901 else
902 {
903 change_batch_type(this, PB_BATCH_NONE);
904 status = INVALID_STATE;
905 }
906 }
907 else
908 {
909 DBG1(DBG_TNC, "no PB-TNC batch to send");
910 status = INVALID_STATE;
911 }
912 this->mutex->unlock(this->mutex);
913
914 return status;
915 }
916
917 METHOD(tls_t, is_server, bool,
918 private_tnccs_20_t *this)
919 {
920 return this->is_server;
921 }
922
923 METHOD(tls_t, get_server_id, identification_t*,
924 private_tnccs_20_t *this)
925 {
926 return this->server;
927 }
928
929 METHOD(tls_t, set_peer_id, void,
930 private_tnccs_20_t *this, identification_t *id)
931 {
932 DESTROY_IF(this->peer);
933 this->peer = id->clone(id);
934 }
935
936 METHOD(tls_t, get_peer_id, identification_t*,
937 private_tnccs_20_t *this)
938 {
939 return this->peer;
940 }
941
942 METHOD(tls_t, get_purpose, tls_purpose_t,
943 private_tnccs_20_t *this)
944 {
945 return TLS_PURPOSE_EAP_TNC;
946 }
947
948 METHOD(tls_t, is_complete, bool,
949 private_tnccs_20_t *this)
950 {
951 TNC_IMV_Action_Recommendation rec;
952 TNC_IMV_Evaluation_Result eval;
953
954 if (this->recs && this->recs->have_recommendation(this->recs, &rec, &eval))
955 {
956 return this->callback ? this->callback(rec, eval) : TRUE;
957 }
958 else
959 {
960 return FALSE;
961 }
962 }
963
964 METHOD(tls_t, get_eap_msk, chunk_t,
965 private_tnccs_20_t *this)
966 {
967 return chunk_empty;
968 }
969
970 METHOD(tls_t, destroy, void,
971 private_tnccs_20_t *this)
972 {
973 if (ref_put(&this->ref))
974 {
975 tnc->tnccs->remove_connection(tnc->tnccs, this->connection_id,
976 this->is_server);
977 this->server->destroy(this->server);
978 this->peer->destroy(this->peer);
979 this->state_machine->destroy(this->state_machine);
980 this->mutex->destroy(this->mutex);
981 this->messages->destroy_offset(this->messages,
982 offsetof(pb_tnc_msg_t, destroy));
983 free(this->pdp_server.ptr);
984 free(this);
985 }
986 }
987
988 METHOD(tnccs_t, get_transport, tnc_ift_type_t,
989 private_tnccs_20_t *this)
990 {
991 return this->transport;
992 }
993
994 METHOD(tnccs_t, set_transport, void,
995 private_tnccs_20_t *this, tnc_ift_type_t transport)
996 {
997 this->transport = transport;
998 }
999
1000 METHOD(tnccs_t, get_auth_type, u_int32_t,
1001 private_tnccs_20_t *this)
1002 {
1003 return this->auth_type;
1004 }
1005
1006 METHOD(tnccs_t, set_auth_type, void,
1007 private_tnccs_20_t *this, u_int32_t auth_type)
1008 {
1009 this->auth_type = auth_type;
1010 }
1011
1012 METHOD(tnccs_t, get_pdp_server, chunk_t,
1013 private_tnccs_20_t *this, u_int16_t *port)
1014 {
1015 *port = this->pdp_port;
1016
1017 return this->pdp_server;
1018 }
1019
1020 METHOD(tnccs_t, get_ref, tnccs_t*,
1021 private_tnccs_20_t *this)
1022 {
1023 ref_get(&this->ref);
1024 return &this->public;
1025 }
1026
1027 /**
1028 * See header
1029 */
1030 tnccs_t* tnccs_20_create(bool is_server,
1031 identification_t *server, identification_t *peer,
1032 tnc_ift_type_t transport, tnccs_cb_t cb)
1033 {
1034 private_tnccs_20_t *this;
1035
1036 INIT(this,
1037 .public = {
1038 .tls = {
1039 .process = _process,
1040 .build = _build,
1041 .is_server = _is_server,
1042 .get_server_id = _get_server_id,
1043 .set_peer_id = _set_peer_id,
1044 .get_peer_id = _get_peer_id,
1045 .get_purpose = _get_purpose,
1046 .is_complete = _is_complete,
1047 .get_eap_msk = _get_eap_msk,
1048 .destroy = _destroy,
1049 },
1050 .get_transport = _get_transport,
1051 .set_transport = _set_transport,
1052 .get_auth_type = _get_auth_type,
1053 .set_auth_type = _set_auth_type,
1054 .get_pdp_server = _get_pdp_server,
1055 .get_ref = _get_ref,
1056 },
1057 .is_server = is_server,
1058 .server = server->clone(server),
1059 .peer = peer->clone(peer),
1060 .transport = transport,
1061 .callback = cb,
1062 .state_machine = pb_tnc_state_machine_create(is_server),
1063 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
1064 .messages = linked_list_create(),
1065 .max_batch_len = lib->settings->get_int(lib->settings,
1066 "libtnccs.plugins.tnccs-20.max_batch_size", 65522),
1067 .max_msg_len = lib->settings->get_int(lib->settings,
1068 "libtnccs.plugins.tnccs-20.max_message_size", 65490),
1069 .ref = 1,
1070 );
1071
1072 return &this->public;
1073 }