]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/libtnccs/plugins/tnccs_20/tnccs_20_server.c
Update copyright headers after acquisition by secunet
[thirdparty/strongswan.git] / src / libtnccs / plugins / tnccs_20 / tnccs_20_server.c
1 /*
2 * Copyright (C) 2015 Andreas Steffen
3 *
4 * Copyright (C) secunet Security Networks AG
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_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"
30
31 #include <tncif_names.h>
32 #include <tncif_pa_subtypes.h>
33
34 #include <tnc/tnc.h>
35 #include <tnc/tnccs/tnccs_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_server_t private_tnccs_20_server_t;
44
45 /**
46 * Private data of a tnccs_20_server_t object.
47 */
48 struct private_tnccs_20_server_t {
49
50 /**
51 * Public tnccs_20_server_t interface.
52 */
53 tnccs_20_server_t public;
54
55 /**
56 * PB-TNC State Machine
57 */
58 pb_tnc_state_machine_t *state_machine;
59
60 /**
61 * Connection ID assigned to this TNCCS connection
62 */
63 TNC_ConnectionID connection_id;
64
65 /**
66 * PB-TNC messages to be sent
67 */
68 linked_list_t *messages;
69
70 /**
71 * Type of PB-TNC batch being constructed
72 */
73 pb_tnc_batch_type_t batch_type;
74
75 /**
76 * Maximum PB-TNC batch size
77 */
78 size_t max_batch_len;
79
80 /**
81 * Mutex locking the batch in construction
82 */
83 mutex_t *mutex;
84
85 /**
86 * Flag set while processing
87 */
88 bool fatal_error;
89
90 /**
91 * Flag set by IMC/IMV RequestHandshakeRetry() function
92 */
93 bool request_handshake_retry;
94
95 /**
96 * Flag set after sending SRETRY batch
97 */
98 bool retry_handshake;
99
100 /**
101 * SendMessage() by IMV only allowed if flag is set
102 */
103 bool send_msg;
104
105 /**
106 * Set of IMV recommendations
107 */
108 recommendations_t *recs;
109
110 /**
111 * TNC IF-T transport protocol for EAP methods
112 */
113 bool eap_transport;
114
115 /**
116 * Mutual PB-TNC protocol enabled
117 */
118 bool mutual;
119
120 /**
121 * Mutual Capability message sent
122 */
123 bool sent_mutual_capability;
124
125 };
126
127 /**
128 * The following two functions are shared with the tnccs_20_server class
129 */
130 extern void tnccs_20_handle_ietf_error_msg(pb_tnc_msg_t *msg,
131 bool *fatal_error);
132 extern bool tnccs_20_handle_ita_mutual_capability_msg(pb_tnc_msg_t *msg);
133
134 /**
135 * If the batch type changes then delete all accumulated PB-TNC messages
136 */
137 static void change_batch_type(private_tnccs_20_server_t *this,
138 pb_tnc_batch_type_t batch_type)
139 {
140 pb_tnc_msg_t *msg;
141
142 if (batch_type != this->batch_type)
143 {
144 if (this->batch_type != PB_BATCH_NONE)
145 {
146 DBG1(DBG_TNC, "canceling PB-TNC %N batch",
147 pb_tnc_batch_type_names, this->batch_type);
148
149 while (this->messages->remove_last(this->messages,
150 (void**)&msg) == SUCCESS)
151 {
152 msg->destroy(msg);
153 }
154 }
155 this->batch_type = batch_type;
156 }
157 }
158
159 /**
160 * Handle a single PB-TNC IETF standard message according to its type
161 */
162 static void handle_ietf_message(private_tnccs_20_server_t *this, pb_tnc_msg_t *msg)
163 {
164 pen_type_t msg_type = msg->get_type(msg);
165
166 switch (msg_type.type)
167 {
168 case PB_MSG_EXPERIMENTAL:
169 /* nothing to do */
170 break;
171 case PB_MSG_PA:
172 {
173 pb_pa_msg_t *pa_msg;
174 pen_type_t msg_subtype;
175 uint16_t imc_id, imv_id;
176 chunk_t msg_body;
177 bool excl;
178 enum_name_t *pa_subtype_names;
179
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);
186
187 pa_subtype_names = get_pa_subtype_names(msg_subtype.vendor_id);
188 if (pa_subtype_names)
189 {
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);
193 }
194 else
195 {
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,
198 msg_subtype.type);
199 }
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;
206 break;
207 }
208 case PB_MSG_ERROR:
209 tnccs_20_handle_ietf_error_msg(msg, &this->fatal_error);
210 break;
211 case PB_MSG_LANGUAGE_PREFERENCE:
212 {
213 pb_language_preference_msg_t *lang_msg;
214 chunk_t lang;
215
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);
221 break;
222 }
223 default:
224 break;
225 }
226 }
227
228 /**
229 * Handle a single PB-TNC ITA standard message according to its type
230 */
231 static void handle_ita_message(private_tnccs_20_server_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_ITA_MSG_MUTUAL_CAPABILITY:
238 this->mutual = tnccs_20_handle_ita_mutual_capability_msg(msg);
239
240 /* Respond with PB-TNC Mutual Capability message if necessary */
241 if (this->mutual && !this->sent_mutual_capability)
242 {
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;
248 }
249 break;
250 default:
251 break;
252 }
253 }
254
255 /**
256 * Handle a single PB-TNC message according to its type
257 */
258 static void handle_message(private_tnccs_20_server_t *this, pb_tnc_msg_t *msg)
259 {
260 pen_type_t msg_type = msg->get_type(msg);
261
262 switch (msg_type.vendor_id)
263 {
264 case PEN_IETF:
265 handle_ietf_message(this, msg);
266 break;
267 case PEN_ITA:
268 handle_ita_message(this, msg);
269 break;
270 default:
271 break;
272 }
273 }
274
275 /**
276 * Build an SRETRY batch
277 */
278 static void build_retry_batch(private_tnccs_20_server_t *this)
279 {
280 if (this->batch_type == PB_BATCH_SRETRY)
281 {
282 /* retry batch has already been selected */
283 return;
284 }
285 change_batch_type(this, PB_BATCH_SRETRY);
286 }
287
288 METHOD(tnccs_20_handler_t, process, status_t,
289 private_tnccs_20_server_t *this, pb_tnc_batch_t *batch)
290 {
291 pb_tnc_batch_type_t batch_type;
292 status_t status;
293
294 batch_type = batch->get_type(batch);
295
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);
299
300 if (status != FAILED)
301 {
302 enumerator_t *enumerator;
303 pb_tnc_msg_t *msg;
304 bool empty = TRUE;
305
306 if (batch_type == PB_BATCH_CRETRY ||
307 (batch_type == PB_BATCH_CDATA && this->retry_handshake))
308 {
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;
313 }
314
315 enumerator = batch->create_msg_enumerator(batch);
316 while (enumerator->enumerate(enumerator, &msg))
317 {
318 handle_message(this, msg);
319 empty = FALSE;
320 }
321 enumerator->destroy(enumerator);
322
323 /* received a CLOSE batch from PB-TNC client */
324 if (batch_type == PB_BATCH_CLOSE)
325 {
326 return empty ? SUCCESS : FAILED;
327 }
328
329 this->send_msg = TRUE;
330 tnc->imvs->batch_ending(tnc->imvs, this->connection_id);
331 this->send_msg = FALSE;
332 }
333
334 switch (status)
335 {
336 case FAILED:
337 this->fatal_error = TRUE;
338 status = VERIFY_ERROR;
339 break;
340 case VERIFY_ERROR:
341 break;
342 case SUCCESS:
343 default:
344 status = NEED_MORE;
345 break;
346 }
347
348 return status;
349 }
350
351 /**
352 * Build a RESULT batch if a final recommendation is available
353 */
354 static void check_and_build_recommendation(private_tnccs_20_server_t *this)
355 {
356 TNC_IMV_Action_Recommendation rec;
357 TNC_IMV_Evaluation_Result eval;
358 TNC_ConnectionState state;
359 TNC_IMVID id;
360 chunk_t reason, language;
361 enumerator_t *enumerator;
362 pb_tnc_msg_t *msg;
363 pb_access_recommendation_code_t pb_rec;
364
365 if (!this->recs->have_recommendation(this->recs, &rec, &eval))
366 {
367 tnc->imvs->solicit_recommendation(tnc->imvs, this->connection_id);
368 }
369 if (this->recs->have_recommendation(this->recs, &rec, &eval))
370 {
371 this->batch_type = PB_BATCH_RESULT;
372
373 msg = pb_assessment_result_msg_create(eval);
374 this->messages->insert_last(this->messages, msg);
375
376 /**
377 * Map IMV Action Recommendation codes to PB Access Recommendation codes
378 * and communicate Access Recommendation to IMVs
379 */
380 switch (rec)
381 {
382 case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
383 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
384 pb_rec = PB_REC_ACCESS_ALLOWED;
385 break;
386 case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
387 state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
388 pb_rec = PB_REC_QUARANTINED;
389 break;
390 case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
391 case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
392 default:
393 state = TNC_CONNECTION_STATE_ACCESS_NONE;
394 pb_rec = PB_REC_ACCESS_DENIED;
395 }
396 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
397 state);
398
399 msg = pb_access_recommendation_msg_create(pb_rec);
400 this->messages->insert_last(this->messages, msg);
401
402 enumerator = this->recs->create_reason_enumerator(this->recs);
403 while (enumerator->enumerate(enumerator, &id, &reason, &language))
404 {
405 msg = pb_reason_string_msg_create(reason, language);
406 this->messages->insert_last(this->messages, msg);
407 }
408 enumerator->destroy(enumerator);
409 }
410 }
411
412 METHOD(tnccs_20_handler_t, build, status_t,
413 private_tnccs_20_server_t *this, void *buf, size_t *buflen, size_t *msglen)
414 {
415 status_t status;
416 pb_tnc_state_t state;
417
418 state = this->state_machine->get_state(this->state_machine);
419
420 if (this->fatal_error && state == PB_STATE_END)
421 {
422 DBG1(DBG_TNC, "a fatal PB-TNC error occurred, terminating connection");
423 return FAILED;
424 }
425
426 /* Do not allow any asynchronous IMVs to add additional messages */
427 this->mutex->lock(this->mutex);
428
429 if (this->request_handshake_retry)
430 {
431 if (state == PB_STATE_DECIDED)
432 {
433 build_retry_batch(this);
434
435 /* Handshake will be retried with next incoming CDATA batch */
436 this->retry_handshake = TRUE;
437 }
438
439 /* Reset the flag for the next handshake retry request */
440 this->request_handshake_retry = FALSE;
441 }
442
443 if (state == PB_STATE_SERVER_WORKING &&
444 this->recs->have_recommendation(this->recs, NULL, NULL))
445 {
446 check_and_build_recommendation(this);
447 }
448
449 if (this->batch_type == PB_BATCH_NONE)
450 {
451 if (state == PB_STATE_SERVER_WORKING)
452 {
453 if (this->state_machine->get_empty_cdata(this->state_machine))
454 {
455 check_and_build_recommendation(this);
456 }
457 else
458 {
459 DBG2(DBG_TNC, "no recommendation available yet, "
460 "sending empty PB-TNC SDATA batch");
461 this->batch_type = PB_BATCH_SDATA;
462 }
463 }
464 }
465
466 if (this->batch_type != PB_BATCH_NONE)
467 {
468 pb_tnc_batch_t *batch;
469 pb_tnc_msg_t *msg;
470 chunk_t data;
471 int msg_count;
472 enumerator_t *enumerator;
473
474 if (this->state_machine->send_batch(this->state_machine, this->batch_type))
475 {
476 batch = pb_tnc_batch_create(TRUE, this->batch_type,
477 min(this->max_batch_len, *buflen));
478
479 enumerator = this->messages->create_enumerator(this->messages);
480 while (enumerator->enumerate(enumerator, &msg))
481 {
482 if (batch->add_msg(batch, msg))
483 {
484 this->messages->remove_at(this->messages, enumerator);
485 }
486 else
487 {
488 break;
489 }
490 }
491 enumerator->destroy(enumerator);
492
493 batch->build(batch);
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);
499
500 *buflen = data.len;
501 *msglen = 0;
502 memcpy(buf, data.ptr, *buflen);
503 batch->destroy(batch);
504
505 msg_count = this->messages->get_count(this->messages);
506 if (msg_count)
507 {
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);
511 }
512 else
513 {
514 this->batch_type = PB_BATCH_NONE;
515 }
516
517 status = ALREADY_DONE;
518 }
519 else
520 {
521 change_batch_type(this, PB_BATCH_NONE);
522 status = INVALID_STATE;
523 }
524 }
525 else
526 {
527 DBG1(DBG_TNC, "no PB-TNC batch to send");
528 status = INVALID_STATE;
529 }
530 this->mutex->unlock(this->mutex);
531
532 return status;
533 }
534
535 METHOD(tnccs_20_handler_t, begin_handshake, void,
536 private_tnccs_20_server_t *this, bool mutual)
537 {
538 pb_tnc_msg_t *msg;
539 identification_t *pdp_server;
540 uint16_t *pdp_port;
541
542 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
543 TNC_CONNECTION_STATE_HANDSHAKE);
544
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");
548
549 if (this->eap_transport && pdp_server && pdp_port)
550 {
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);
556 }
557
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))
561 {
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);
566 }
567 }
568
569 METHOD(tnccs_20_handler_t, get_send_flag, bool,
570 private_tnccs_20_server_t *this)
571 {
572 return this->send_msg;
573 }
574
575 METHOD(tnccs_20_handler_t, get_mutual, bool,
576 private_tnccs_20_server_t *this)
577 {
578 return this->mutual;
579 }
580
581 METHOD(tnccs_20_handler_t, get_state, pb_tnc_state_t,
582 private_tnccs_20_server_t *this)
583 {
584 return this->state_machine->get_state(this->state_machine);
585 }
586
587 METHOD(tnccs_20_handler_t, add_msg, void,
588 private_tnccs_20_server_t *this, pb_tnc_msg_t *msg)
589 {
590 /* adding PA message to SDATA batch only */
591 this->mutex->lock(this->mutex);
592 if (this->batch_type == PB_BATCH_NONE)
593 {
594 this->batch_type = PB_BATCH_SDATA;
595 }
596 if (this->batch_type == PB_BATCH_SDATA)
597 {
598 this->messages->insert_last(this->messages, msg);
599 }
600 else
601 {
602 msg->destroy(msg);
603 }
604 this->mutex->unlock(this->mutex);
605 }
606
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)
610 {
611 pb_tnc_msg_t *msg;
612 enumerator_t *enumerator;
613
614 if (fatal_header_error || this->fatal_error)
615 {
616 this->mutex->lock(this->mutex);
617 change_batch_type(this, PB_BATCH_CLOSE);
618 this->mutex->unlock(this->mutex);
619 }
620
621 enumerator = batch->create_error_enumerator(batch);
622 while (enumerator->enumerate(enumerator, &msg))
623 {
624 this->mutex->lock(this->mutex);
625 this->messages->insert_last(this->messages, msg->get_ref(msg));
626 this->mutex->unlock(this->mutex);
627 }
628 enumerator->destroy(enumerator);
629 }
630
631 METHOD(tnccs_20_handler_t, destroy, void,
632 private_tnccs_20_server_t *this)
633 {
634 if (this->connection_id)
635 {
636 tnc->tnccs->remove_connection(tnc->tnccs, this->connection_id, TRUE);
637 }
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));
642 free(this);
643 }
644
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)
648 {
649 return this->recs->have_recommendation(this->recs, rec, eval);
650 }
651
652 /**
653 * See header
654 */
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,
658 size_t max_msg_len,
659 bool eap_transport)
660 {
661 private_tnccs_20_server_t *this;
662
663 INIT(this,
664 .public = {
665 .handler = {
666 .process = _process,
667 .build = _build,
668 .begin_handshake = _begin_handshake,
669 .get_send_flag = _get_send_flag,
670 .get_mutual = _get_mutual,
671 .get_state = _get_state,
672 .add_msg = _add_msg,
673 .handle_errors = _handle_errors,
674 .destroy = _destroy,
675 },
676 .have_recommendation = _have_recommendation,
677 },
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,
684 );
685
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)
691 {
692 destroy(this);
693 return NULL;
694 }
695 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
696 TNC_CONNECTION_STATE_CREATE);
697
698 return &this->public.handler;
699 }