Keep a copy of the tnccs instance for PT-TLS handover
[strongswan.git] / src / libtnccs / plugins / tnccs_11 / tnccs_11.c
1 /*
2 * Copyright (C) 2010-2013 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
4 *
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>.
9 *
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
13 * for more details.
14 */
15
16 #include "tnccs_11.h"
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"
24
25 #include <tncif_names.h>
26 #include <tncif_pa_subtypes.h>
27
28 #include <tnc/tnc.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>
33
34 #include <utils/debug.h>
35 #include <threading/mutex.h>
36
37 typedef struct private_tnccs_11_t private_tnccs_11_t;
38
39 /**
40 * Private data of a tnccs_11_t object.
41 */
42 struct private_tnccs_11_t {
43
44 /**
45 * Public tnccs_t interface.
46 */
47 tnccs_t public;
48
49 /**
50 * TNCC if TRUE, TNCS if FALSE
51 */
52 bool is_server;
53
54 /**
55 * Server identity
56 */
57 identification_t *server;
58
59 /**
60 * Client identity
61 */
62 identification_t *peer;
63
64 /**
65 * Underlying TNC IF-T transport protocol
66 */
67 tnc_ift_type_t transport;
68
69 /**
70 * Type of TNC client authentication
71 */
72 u_int32_t auth_type;
73
74 /**
75 * Connection ID assigned to this TNCCS connection
76 */
77 TNC_ConnectionID connection_id;
78
79 /**
80 * Last TNCCS batch ID
81 */
82 int batch_id;
83
84 /**
85 * TNCCS batch being constructed
86 */
87 tnccs_batch_t *batch;
88
89 /**
90 * Maximum PA-TNC message size
91 */
92 size_t max_msg_len;
93
94 /**
95 * Mutex locking the batch in construction
96 */
97 mutex_t *mutex;
98
99 /**
100 * Flag set while processing
101 */
102 bool fatal_error;
103
104 /**
105 * Flag set by TNCCS-Recommendation message
106 */
107 bool delete_state;
108
109 /**
110 * SendMessage() by IMC/IMV only allowed if flag is set
111 */
112 bool send_msg;
113
114 /**
115 * Flag set by IMC/IMV RequestHandshakeRetry() function
116 */
117 bool request_handshake_retry;
118
119 /**
120 * Set of IMV recommendations (TNC Server only)
121 */
122 recommendations_t *recs;
123
124 /**
125 * Callback function to communicate recommendation (TNC Server only)
126 */
127 tnccs_cb_t callback;
128
129 /**
130 * reference count
131 */
132 refcount_t ref;
133
134 };
135
136 METHOD(tnccs_t, send_msg, TNC_Result,
137 private_tnccs_11_t* this, TNC_IMCID imc_id, TNC_IMVID imv_id,
138 TNC_UInt32 msg_flags,
139 TNC_BufferReference msg,
140 TNC_UInt32 msg_len,
141 TNC_VendorID msg_vid,
142 TNC_MessageSubtype msg_subtype)
143 {
144 tnccs_msg_t *tnccs_msg;
145 TNC_MessageType msg_type;
146 enum_name_t *pa_subtype_names;
147
148 if (!this->send_msg)
149 {
150 DBG1(DBG_TNC, "%s %u not allowed to call SendMessage()",
151 this->is_server ? "IMV" : "IMC",
152 this->is_server ? imv_id : imc_id);
153 return TNC_RESULT_ILLEGAL_OPERATION;
154 }
155 if (msg_vid > TNC_VENDORID_ANY || msg_subtype > TNC_SUBTYPE_ANY)
156 {
157 return TNC_RESULT_NO_LONG_MESSAGE_TYPES;
158 }
159 msg_type = (msg_vid << 8) | msg_subtype;
160
161 pa_subtype_names = get_pa_subtype_names(msg_vid);
162 if (pa_subtype_names)
163 {
164 DBG2(DBG_TNC, "creating IMC-IMV message type '%N/%N' 0x%06x/0x%02x",
165 pen_names, msg_vid, pa_subtype_names, msg_subtype,
166 msg_vid, msg_subtype);
167 }
168 else
169 {
170 DBG2(DBG_TNC, "creating IMC-IMV message type '%N' 0x%06x/0x%02x",
171 pen_names, msg_vid, msg_vid, msg_subtype);
172 }
173 tnccs_msg = imc_imv_msg_create(msg_type, chunk_create(msg, msg_len));
174
175 /* adding an IMC-IMV Message to TNCCS batch */
176 this->mutex->lock(this->mutex);
177 if (!this->batch)
178 {
179 this->batch = tnccs_batch_create(this->is_server, ++this->batch_id);
180 }
181 this->batch->add_msg(this->batch, tnccs_msg);
182 this->mutex->unlock(this->mutex);
183 return TNC_RESULT_SUCCESS;
184 }
185
186 /**
187 * Handle a single TNCCS message according to its type
188 */
189 static void handle_message(private_tnccs_11_t *this, tnccs_msg_t *msg)
190 {
191 switch (msg->get_type(msg))
192 {
193 case IMC_IMV_MSG:
194 {
195 imc_imv_msg_t *imc_imv_msg;
196 TNC_MessageType msg_type;
197 chunk_t msg_body;
198 u_int32_t msg_vid, msg_subtype;
199 enum_name_t *pa_subtype_names;
200
201 imc_imv_msg = (imc_imv_msg_t*)msg;
202 msg_type = imc_imv_msg->get_msg_type(imc_imv_msg);
203 msg_body = imc_imv_msg->get_msg_body(imc_imv_msg);
204 msg_vid = (msg_type >> 8) & TNC_VENDORID_ANY;
205 msg_subtype = msg_type & TNC_SUBTYPE_ANY;
206
207 pa_subtype_names = get_pa_subtype_names(msg_vid);
208 if (pa_subtype_names)
209 {
210 DBG2(DBG_TNC, "handling IMC-IMV message type '%N/%N' 0x%06x/0x%02x",
211 pen_names, msg_vid, pa_subtype_names, msg_subtype,
212 msg_vid, msg_subtype);
213 }
214 else
215 {
216 DBG2(DBG_TNC, "handling IMC-IMV message type '%N' 0x%06x/0x%02x",
217 pen_names, msg_vid, msg_vid, msg_subtype);
218 }
219
220 this->send_msg = TRUE;
221 if (this->is_server)
222 {
223 tnc->imvs->receive_message(tnc->imvs, this->connection_id,
224 FALSE, msg_body.ptr, msg_body.len,
225 msg_vid, msg_subtype, 0, TNC_IMVID_ANY);
226 }
227 else
228 {
229 tnc->imcs->receive_message(tnc->imcs, this->connection_id,
230 FALSE, msg_body.ptr, msg_body.len,
231 msg_vid, msg_subtype, 0, TNC_IMCID_ANY);
232 }
233 this->send_msg = FALSE;
234 break;
235 }
236 case TNCCS_MSG_RECOMMENDATION:
237 {
238 tnccs_recommendation_msg_t *rec_msg;
239 TNC_IMV_Action_Recommendation rec;
240 TNC_ConnectionState state = TNC_CONNECTION_STATE_ACCESS_NONE;
241
242 rec_msg = (tnccs_recommendation_msg_t*)msg;
243 rec = rec_msg->get_recommendation(rec_msg);
244 if (this->is_server)
245 {
246 DBG1(DBG_TNC, "ignoring NCCS-Recommendation message from "
247 " TNC client");
248 break;
249 }
250 DBG1(DBG_TNC, "TNC recommendation is '%N'",
251 TNC_IMV_Action_Recommendation_names, rec);
252 switch (rec)
253 {
254 case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
255 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
256 break;
257 case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
258 state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
259 break;
260 case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
261 default:
262 state = TNC_CONNECTION_STATE_ACCESS_NONE;
263 }
264 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
265 state);
266 this->delete_state = TRUE;
267 break;
268 }
269 case TNCCS_MSG_ERROR:
270 {
271 tnccs_error_msg_t *err_msg;
272 tnccs_error_type_t error_type;
273 char *error_msg;
274
275 err_msg = (tnccs_error_msg_t*)msg;
276 error_msg = err_msg->get_message(err_msg, &error_type);
277 DBG1(DBG_TNC, "received '%N' TNCCS-Error: %s",
278 tnccs_error_type_names, error_type, error_msg);
279
280 /* we assume that all errors are fatal */
281 this->fatal_error = TRUE;
282 break;
283 }
284 case TNCCS_MSG_PREFERRED_LANGUAGE:
285 {
286 tnccs_preferred_language_msg_t *lang_msg;
287 char *lang;
288
289 lang_msg = (tnccs_preferred_language_msg_t*)msg;
290 lang = lang_msg->get_preferred_language(lang_msg);
291
292 DBG2(DBG_TNC, "setting preferred language to '%s'", lang);
293 this->recs->set_preferred_language(this->recs,
294 chunk_create(lang, strlen(lang)));
295 break;
296 }
297 case TNCCS_MSG_REASON_STRINGS:
298 {
299 tnccs_reason_strings_msg_t *reason_msg;
300 chunk_t reason_string, reason_lang;
301
302 reason_msg = (tnccs_reason_strings_msg_t*)msg;
303 reason_string = reason_msg->get_reason(reason_msg, &reason_lang);
304 DBG2(DBG_TNC, "reason string is '%.*s'", (int)reason_string.len,
305 reason_string.ptr);
306 DBG2(DBG_TNC, "language code is '%.*s'", (int)reason_lang.len,
307 reason_lang.ptr);
308 break;
309 }
310 default:
311 break;
312 }
313 }
314
315 METHOD(tls_t, process, status_t,
316 private_tnccs_11_t *this, void *buf, size_t buflen)
317 {
318 chunk_t data;
319 tnccs_batch_t *batch;
320 tnccs_msg_t *msg;
321 enumerator_t *enumerator;
322 status_t status;
323
324 if (this->is_server && !this->connection_id)
325 {
326 this->connection_id = tnc->tnccs->create_connection(tnc->tnccs,
327 TNCCS_1_1, (tnccs_t*)this, _send_msg,
328 &this->request_handshake_retry,
329 this->max_msg_len, &this->recs);
330 if (!this->connection_id)
331 {
332 return FAILED;
333 }
334 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
335 TNC_CONNECTION_STATE_CREATE);
336 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
337 TNC_CONNECTION_STATE_HANDSHAKE);
338 }
339
340 data = chunk_create(buf, buflen);
341 DBG1(DBG_TNC, "received TNCCS Batch (%u bytes) for Connection ID %u",
342 data.len, this->connection_id);
343 DBG3(DBG_TNC, "%.*s", (int)data.len, data.ptr);
344 batch = tnccs_batch_create_from_data(this->is_server, ++this->batch_id, data);
345 status = batch->process(batch);
346
347 if (status == FAILED)
348 {
349 this->fatal_error = TRUE;
350 this->mutex->lock(this->mutex);
351 if (this->batch)
352 {
353 DBG1(DBG_TNC, "cancelling TNCCS batch");
354 this->batch->destroy(this->batch);
355 this->batch_id--;
356 }
357 this->batch = tnccs_batch_create(this->is_server, ++this->batch_id);
358
359 /* add error messages to outbound batch */
360 enumerator = batch->create_error_enumerator(batch);
361 while (enumerator->enumerate(enumerator, &msg))
362 {
363 this->batch->add_msg(this->batch, msg->get_ref(msg));
364 }
365 enumerator->destroy(enumerator);
366 this->mutex->unlock(this->mutex);
367 }
368 else
369 {
370 enumerator = batch->create_msg_enumerator(batch);
371 while (enumerator->enumerate(enumerator, &msg))
372 {
373 handle_message(this, msg);
374 }
375 enumerator->destroy(enumerator);
376
377 /* received any TNCCS-Error messages */
378 if (this->fatal_error)
379 {
380 DBG1(DBG_TNC, "a fatal TNCCS-Error occurred, terminating connection");
381 batch->destroy(batch);
382 return FAILED;
383 }
384
385 this->send_msg = TRUE;
386 if (this->is_server)
387 {
388 tnc->imvs->batch_ending(tnc->imvs, this->connection_id);
389 }
390 else
391 {
392 tnc->imcs->batch_ending(tnc->imcs, this->connection_id);
393 }
394 this->send_msg = FALSE;
395 }
396 batch->destroy(batch);
397
398 return NEED_MORE;
399 }
400
401 /**
402 * Add a recommendation message if a final recommendation is available
403 */
404 static void check_and_build_recommendation(private_tnccs_11_t *this)
405 {
406 TNC_IMV_Action_Recommendation rec;
407 TNC_IMV_Evaluation_Result eval;
408 TNC_IMVID id;
409 chunk_t reason, language;
410 enumerator_t *enumerator;
411 tnccs_msg_t *msg;
412
413 if (!this->recs->have_recommendation(this->recs, &rec, &eval))
414 {
415 tnc->imvs->solicit_recommendation(tnc->imvs, this->connection_id);
416 }
417 if (this->recs->have_recommendation(this->recs, &rec, &eval))
418 {
419 if (!this->batch)
420 {
421 this->batch = tnccs_batch_create(this->is_server, ++this->batch_id);
422 }
423
424 msg = tnccs_recommendation_msg_create(rec);
425 this->batch->add_msg(this->batch, msg);
426
427 /* currently we just send the first Reason String */
428 enumerator = this->recs->create_reason_enumerator(this->recs);
429 if (enumerator->enumerate(enumerator, &id, &reason, &language))
430 {
431 msg = tnccs_reason_strings_msg_create(reason, language);
432 this->batch->add_msg(this->batch, msg);
433 }
434 enumerator->destroy(enumerator);
435
436 /* we have reache the final state */
437 this->delete_state = TRUE;
438 }
439 }
440
441 METHOD(tls_t, build, status_t,
442 private_tnccs_11_t *this, void *buf, size_t *buflen, size_t *msglen)
443 {
444 status_t status;
445
446 /* Initialize the connection */
447 if (!this->is_server && !this->connection_id)
448 {
449 tnccs_msg_t *msg;
450 char *pref_lang;
451
452 this->connection_id = tnc->tnccs->create_connection(tnc->tnccs,
453 TNCCS_1_1, (tnccs_t*)this, _send_msg,
454 &this->request_handshake_retry,
455 this->max_msg_len, NULL);
456 if (!this->connection_id)
457 {
458 return FAILED;
459 }
460
461 /* Create TNCCS-PreferredLanguage message */
462 pref_lang = tnc->imcs->get_preferred_language(tnc->imcs);
463 msg = tnccs_preferred_language_msg_create(pref_lang);
464 this->mutex->lock(this->mutex);
465 this->batch = tnccs_batch_create(this->is_server, ++this->batch_id);
466 this->batch->add_msg(this->batch, msg);
467 this->mutex->unlock(this->mutex);
468
469 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
470 TNC_CONNECTION_STATE_CREATE);
471 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
472 TNC_CONNECTION_STATE_HANDSHAKE);
473 this->send_msg = TRUE;
474 tnc->imcs->begin_handshake(tnc->imcs, this->connection_id);
475 this->send_msg = FALSE;
476 }
477
478 /* Do not allow any asynchronous IMCs or IMVs to add additional messages */
479 this->mutex->lock(this->mutex);
480
481 if (this->recs && !this->delete_state &&
482 (!this->batch || this->fatal_error))
483 {
484 check_and_build_recommendation(this);
485 }
486
487 if (this->batch)
488 {
489 chunk_t data;
490
491 this->batch->build(this->batch);
492 data = this->batch->get_encoding(this->batch);
493 DBG1(DBG_TNC, "sending TNCCS Batch (%d bytes) for Connection ID %u",
494 data.len, this->connection_id);
495 DBG3(DBG_TNC, "%.*s", (int)data.len, data.ptr);
496 *msglen = 0;
497
498 if (data.len > *buflen)
499 {
500 DBG1(DBG_TNC, "fragmentation of TNCCS batch not supported yet");
501 }
502 else
503 {
504 *buflen = data.len;
505 }
506 memcpy(buf, data.ptr, *buflen);
507 this->batch->destroy(this->batch);
508 this->batch = NULL;
509 status = ALREADY_DONE;
510 }
511 else
512 {
513 DBG1(DBG_TNC, "no TNCCS Batch to send");
514 status = INVALID_STATE;
515 }
516 this->mutex->unlock(this->mutex);
517
518 return status;
519 }
520
521 METHOD(tls_t, is_server, bool,
522 private_tnccs_11_t *this)
523 {
524 return this->is_server;
525 }
526
527 METHOD(tls_t, get_server_id, identification_t*,
528 private_tnccs_11_t *this)
529 {
530 return this->server;
531 }
532
533 METHOD(tls_t, set_peer_id, void,
534 private_tnccs_11_t *this, identification_t *id)
535 {
536 DESTROY_IF(this->peer);
537 this->peer = id->clone(id);
538 }
539
540 METHOD(tls_t, get_peer_id, identification_t*,
541 private_tnccs_11_t *this)
542 {
543 return this->peer;
544 }
545
546 METHOD(tls_t, get_purpose, tls_purpose_t,
547 private_tnccs_11_t *this)
548 {
549 return TLS_PURPOSE_EAP_TNC;
550 }
551
552 METHOD(tls_t, is_complete, bool,
553 private_tnccs_11_t *this)
554 {
555 TNC_IMV_Action_Recommendation rec;
556 TNC_IMV_Evaluation_Result eval;
557
558 if (this->recs && this->recs->have_recommendation(this->recs, &rec, &eval))
559 {
560 return this->callback ? this->callback(rec, eval) : TRUE;
561 }
562 else
563 {
564 return FALSE;
565 }
566 }
567
568 METHOD(tls_t, get_eap_msk, chunk_t,
569 private_tnccs_11_t *this)
570 {
571 return chunk_empty;
572 }
573
574 METHOD(tls_t, destroy, void,
575 private_tnccs_11_t *this)
576 {
577 if (ref_put(&this->ref))
578 {
579 tnc->tnccs->remove_connection(tnc->tnccs, this->connection_id,
580 this->is_server);
581 this->server->destroy(this->server);
582 this->peer->destroy(this->peer);
583 this->mutex->destroy(this->mutex);
584 DESTROY_IF(this->batch);
585 free(this);
586 }
587 }
588
589 METHOD(tnccs_t, get_transport, tnc_ift_type_t,
590 private_tnccs_11_t *this)
591 {
592 return this->transport;
593 }
594
595 METHOD(tnccs_t, set_transport, void,
596 private_tnccs_11_t *this, tnc_ift_type_t transport)
597 {
598 this->transport = transport;
599 }
600
601 METHOD(tnccs_t, get_auth_type, u_int32_t,
602 private_tnccs_11_t *this)
603 {
604 return this->auth_type;
605 }
606
607 METHOD(tnccs_t, set_auth_type, void,
608 private_tnccs_11_t *this, u_int32_t auth_type)
609 {
610 this->auth_type = auth_type;
611 }
612
613 METHOD(tnccs_t, get_pdp_server, chunk_t,
614 private_tnccs_11_t *this, u_int16_t *port)
615 {
616 *port = 0;
617
618 return chunk_empty;
619 }
620
621 METHOD(tnccs_t, get_ref, tnccs_t*,
622 private_tnccs_11_t *this)
623 {
624 ref_get(&this->ref);
625 return &this->public;
626 }
627
628 /**
629 * See header
630 */
631 tnccs_t* tnccs_11_create(bool is_server,
632 identification_t *server, identification_t *peer,
633 tnc_ift_type_t transport, tnccs_cb_t cb)
634 {
635 private_tnccs_11_t *this;
636
637 INIT(this,
638 .public = {
639 .tls = {
640 .process = _process,
641 .build = _build,
642 .is_server = _is_server,
643 .get_server_id = _get_server_id,
644 .set_peer_id = _set_peer_id,
645 .get_peer_id = _get_peer_id,
646 .get_purpose = _get_purpose,
647 .is_complete = _is_complete,
648 .get_eap_msk = _get_eap_msk,
649 .destroy = _destroy,
650 },
651 .get_transport = _get_transport,
652 .set_transport = _set_transport,
653 .get_auth_type = _get_auth_type,
654 .set_auth_type = _set_auth_type,
655 .get_pdp_server = _get_pdp_server,
656 .get_ref = _get_ref,
657 },
658 .is_server = is_server,
659 .server = server->clone(server),
660 .peer = peer->clone(peer),
661 .transport = transport,
662 .callback = cb,
663 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
664 .max_msg_len = lib->settings->get_int(lib->settings,
665 "libtnccs.plugins.tnccs-11.max_message_size", 45000),
666 .ref = 1,
667 );
668
669 return &this->public;
670 }