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