Moved data structures to new collections subfolder
[strongswan.git] / src / libcharon / plugins / tnc_tnccs / tnc_tnccs_manager.c
1 /*
2 * Copyright (C) 2010-2012 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 "tnc_tnccs_manager.h"
17
18 #include <tnc/tnc.h>
19 #include <tnc/imv/imv_manager.h>
20 #include <tnc/imc/imc_manager.h>
21 #include <tnc/imv/imv_manager.h>
22
23 #include <debug.h>
24 #include <collections/linked_list.h>
25 #include <threading/rwlock.h>
26
27 typedef struct private_tnc_tnccs_manager_t private_tnc_tnccs_manager_t;
28 typedef struct tnccs_entry_t tnccs_entry_t;
29 typedef struct tnccs_connection_entry_t tnccs_connection_entry_t;
30
31 /**
32 * TNCCS constructor entry
33 */
34 struct tnccs_entry_t {
35
36 /**
37 * TNCCS protocol type
38 */
39 tnccs_type_t type;
40
41 /**
42 * constructor function to create instance
43 */
44 tnccs_constructor_t constructor;
45 };
46
47 /**
48 * TNCCS connection entry
49 */
50 struct tnccs_connection_entry_t {
51
52 /**
53 * TNCCS connection ID
54 */
55 TNC_ConnectionID id;
56
57 /**
58 * TNCCS protocol type
59 */
60 tnccs_type_t type;
61
62 /**
63 * TNCCS instance
64 */
65 tnccs_t *tnccs;
66
67 /**
68 * TNCCS send message function
69 */
70 tnccs_send_message_t send_message;
71
72 /**
73 * TNCCS request handshake retry flag
74 */
75 bool *request_handshake_retry;
76
77 /**
78 * Maximum size of a PA-TNC message
79 */
80 u_int32_t max_msg_len;
81
82 /**
83 * collection of IMV recommendations
84 */
85 recommendations_t *recs;
86 };
87
88 /**
89 * private data of tnc_tnccs_manager
90 */
91 struct private_tnc_tnccs_manager_t {
92
93 /**
94 * public functions
95 */
96 tnccs_manager_t public;
97
98 /**
99 * list of TNCCS protocol entries
100 */
101 linked_list_t *protocols;
102
103 /**
104 * rwlock to lock the TNCCS protocol entries
105 */
106 rwlock_t *protocol_lock;
107
108 /**
109 * connection ID counter
110 */
111 TNC_ConnectionID connection_id;
112
113 /**
114 * list of TNCCS connection entries
115 */
116 linked_list_t *connections;
117
118 /**
119 * rwlock to lock TNCCS connection entries
120 */
121 rwlock_t *connection_lock;
122
123 };
124
125 METHOD(tnccs_manager_t, add_method, void,
126 private_tnc_tnccs_manager_t *this, tnccs_type_t type,
127 tnccs_constructor_t constructor)
128 {
129 tnccs_entry_t *entry;
130
131 entry = malloc_thing(tnccs_entry_t);
132 entry->type = type;
133 entry->constructor = constructor;
134
135 this->protocol_lock->write_lock(this->protocol_lock);
136 this->protocols->insert_last(this->protocols, entry);
137 this->protocol_lock->unlock(this->protocol_lock);
138 }
139
140 METHOD(tnccs_manager_t, remove_method, void,
141 private_tnc_tnccs_manager_t *this, tnccs_constructor_t constructor)
142 {
143 enumerator_t *enumerator;
144 tnccs_entry_t *entry;
145
146 this->protocol_lock->write_lock(this->protocol_lock);
147 enumerator = this->protocols->create_enumerator(this->protocols);
148 while (enumerator->enumerate(enumerator, &entry))
149 {
150 if (constructor == entry->constructor)
151 {
152 this->protocols->remove_at(this->protocols, enumerator);
153 free(entry);
154 }
155 }
156 enumerator->destroy(enumerator);
157 this->protocol_lock->unlock(this->protocol_lock);
158 }
159
160 METHOD(tnccs_manager_t, create_instance, tnccs_t*,
161 private_tnc_tnccs_manager_t *this, tnccs_type_t type, bool is_server)
162 {
163 enumerator_t *enumerator;
164 tnccs_entry_t *entry;
165 tnccs_t *protocol = NULL;
166
167 this->protocol_lock->read_lock(this->protocol_lock);
168 enumerator = this->protocols->create_enumerator(this->protocols);
169 while (enumerator->enumerate(enumerator, &entry))
170 {
171 if (type == entry->type)
172 {
173 protocol = entry->constructor(is_server);
174 if (protocol)
175 {
176 break;
177 }
178 }
179 }
180 enumerator->destroy(enumerator);
181 this->protocol_lock->unlock(this->protocol_lock);
182
183 return protocol;
184 }
185
186 METHOD(tnccs_manager_t, create_connection, TNC_ConnectionID,
187 private_tnc_tnccs_manager_t *this, tnccs_type_t type, tnccs_t *tnccs,
188 tnccs_send_message_t send_message, bool* request_handshake_retry,
189 u_int32_t max_msg_len, recommendations_t **recs)
190 {
191 tnccs_connection_entry_t *entry;
192
193 entry = malloc_thing(tnccs_connection_entry_t);
194 entry->type = type;
195 entry->tnccs = tnccs;
196 entry->send_message = send_message;
197 entry->request_handshake_retry = request_handshake_retry;
198 entry->max_msg_len = max_msg_len;
199 if (recs)
200 {
201 /* we assume a TNC Server needing recommendations from IMVs */
202 if (!tnc->imvs)
203 {
204 DBG1(DBG_TNC, "no IMV manager available!");
205 free(entry);
206 return 0;
207 }
208 entry->recs = tnc->imvs->create_recommendations(tnc->imvs);
209 *recs = entry->recs;
210 }
211 else
212 {
213 /* we assume a TNC Client */
214 if (!tnc->imcs)
215 {
216 DBG1(DBG_TNC, "no IMC manager available!");
217 free(entry);
218 return 0;
219 }
220 entry->recs = NULL;
221 }
222 this->connection_lock->write_lock(this->connection_lock);
223 entry->id = ++this->connection_id;
224 this->connections->insert_last(this->connections, entry);
225 this->connection_lock->unlock(this->connection_lock);
226
227 DBG1(DBG_TNC, "assigned TNCCS Connection ID %u", entry->id);
228 return entry->id;
229 }
230
231 METHOD(tnccs_manager_t, remove_connection, void,
232 private_tnc_tnccs_manager_t *this, TNC_ConnectionID id, bool is_server)
233 {
234 enumerator_t *enumerator;
235 tnccs_connection_entry_t *entry;
236
237 if (is_server)
238 {
239 if (tnc->imvs)
240 {
241 tnc->imvs->notify_connection_change(tnc->imvs, id,
242 TNC_CONNECTION_STATE_DELETE);
243 }
244 }
245 else
246 {
247 if (tnc->imcs)
248 {
249 tnc->imcs->notify_connection_change(tnc->imcs, id,
250 TNC_CONNECTION_STATE_DELETE);
251 }
252 }
253
254 this->connection_lock->write_lock(this->connection_lock);
255 enumerator = this->connections->create_enumerator(this->connections);
256 while (enumerator->enumerate(enumerator, &entry))
257 {
258 if (id == entry->id)
259 {
260 this->connections->remove_at(this->connections, enumerator);
261 if (entry->recs)
262 {
263 entry->recs->destroy(entry->recs);
264 }
265 free(entry);
266 DBG1(DBG_TNC, "removed TNCCS Connection ID %u", id);
267 }
268 }
269 enumerator->destroy(enumerator);
270 this->connection_lock->unlock(this->connection_lock);
271 }
272
273 METHOD(tnccs_manager_t, request_handshake_retry, TNC_Result,
274 private_tnc_tnccs_manager_t *this, bool is_imc, TNC_UInt32 imcv_id,
275 TNC_ConnectionID id,
276 TNC_RetryReason reason)
277 {
278 enumerator_t *enumerator;
279 tnccs_connection_entry_t *entry;
280
281 if (id == TNC_CONNECTIONID_ANY)
282 {
283 DBG2(DBG_TNC, "%s %u requests handshake retry for all connections "
284 "(reason: %u)", is_imc ? "IMC":"IMV", reason);
285 }
286 else
287 {
288 DBG2(DBG_TNC, "%s %u requests handshake retry for Connection ID %u "
289 "(reason: %u)", is_imc ? "IMC":"IMV", imcv_id, id, reason);
290 }
291 this->connection_lock->read_lock(this->connection_lock);
292 enumerator = this->connections->create_enumerator(this->connections);
293 while (enumerator->enumerate(enumerator, &entry))
294 {
295 if (id == TNC_CONNECTIONID_ANY || id == entry->id)
296 {
297 *entry->request_handshake_retry = TRUE;
298 break;
299 }
300 }
301 enumerator->destroy(enumerator);
302 this->connection_lock->unlock(this->connection_lock);
303
304 return TNC_RESULT_SUCCESS;
305 }
306
307 METHOD(tnccs_manager_t, send_message, TNC_Result,
308 private_tnc_tnccs_manager_t *this, TNC_IMCID imc_id, TNC_IMVID imv_id,
309 TNC_ConnectionID id,
310 TNC_UInt32 msg_flags,
311 TNC_BufferReference msg,
312 TNC_UInt32 msg_len,
313 TNC_VendorID msg_vid,
314 TNC_MessageSubtype msg_subtype)
315
316 {
317 enumerator_t *enumerator;
318 tnccs_connection_entry_t *entry;
319 tnccs_send_message_t send_message = NULL;
320 tnccs_t *tnccs = NULL;
321
322 if (msg_vid == TNC_VENDORID_ANY || msg_subtype == TNC_SUBTYPE_ANY)
323 {
324 DBG1(DBG_TNC, "not sending message of invalid type 0x%02x/0x%08x",
325 msg_vid, msg_subtype);
326 return TNC_RESULT_INVALID_PARAMETER;
327 }
328
329 this->connection_lock->read_lock(this->connection_lock);
330 enumerator = this->connections->create_enumerator(this->connections);
331 while (enumerator->enumerate(enumerator, &entry))
332 {
333 if (id == entry->id)
334 {
335 tnccs = entry->tnccs;
336 send_message = entry->send_message;
337 break;
338 }
339 }
340 enumerator->destroy(enumerator);
341 this->connection_lock->unlock(this->connection_lock);
342
343 if (tnccs && send_message)
344 {
345 return send_message(tnccs, imc_id, imv_id, msg_flags, msg, msg_len,
346 msg_vid, msg_subtype);
347 }
348 return TNC_RESULT_FATAL;
349 }
350
351 METHOD(tnccs_manager_t, provide_recommendation, TNC_Result,
352 private_tnc_tnccs_manager_t *this, TNC_IMVID imv_id,
353 TNC_ConnectionID id,
354 TNC_IMV_Action_Recommendation rec,
355 TNC_IMV_Evaluation_Result eval)
356 {
357 enumerator_t *enumerator;
358 tnccs_connection_entry_t *entry;
359 recommendations_t *recs = NULL;
360
361 this->connection_lock->read_lock(this->connection_lock);
362 enumerator = this->connections->create_enumerator(this->connections);
363 while (enumerator->enumerate(enumerator, &entry))
364 {
365 if (id == entry->id)
366 {
367 recs = entry->recs;
368 break;
369 }
370 }
371 enumerator->destroy(enumerator);
372 this->connection_lock->unlock(this->connection_lock);
373
374 if (recs)
375 {
376 recs->provide_recommendation(recs, imv_id, rec, eval);
377 return TNC_RESULT_SUCCESS;
378 }
379 return TNC_RESULT_FATAL;
380 }
381
382 /**
383 * Write the value of a boolean attribute into the buffer
384 */
385 static TNC_Result bool_attribute(TNC_UInt32 buffer_len,
386 TNC_BufferReference buffer,
387 TNC_UInt32 *value_len,
388 bool value)
389 {
390 *value_len = 1;
391
392 if (buffer && buffer_len > 0)
393 {
394 *buffer = value ? 0x01 : 0x00;
395 return TNC_RESULT_SUCCESS;
396 }
397 else
398 {
399 return TNC_RESULT_INVALID_PARAMETER;
400 }
401 }
402
403 /**
404 * Write the value of an u_int32_t attribute into the buffer
405 */
406 static TNC_Result uint_attribute(TNC_UInt32 buffer_len,
407 TNC_BufferReference buffer,
408 TNC_UInt32 *value_len,
409 u_int32_t value)
410 {
411 *value_len = sizeof(u_int32_t);
412
413 if (buffer && buffer_len >= *value_len)
414 {
415 htoun32(buffer, value);
416 return TNC_RESULT_SUCCESS;
417 }
418 else
419 {
420 return TNC_RESULT_INVALID_PARAMETER;
421 }
422 }
423
424 /**
425 * Write the value of string attribute into the buffer
426 */
427 static TNC_Result str_attribute(TNC_UInt32 buffer_len,
428 TNC_BufferReference buffer,
429 TNC_UInt32 *value_len,
430 char *value)
431 {
432 *value_len = 1 + strlen(value);
433
434 if (buffer && buffer_len >= *value_len)
435 {
436 snprintf(buffer, buffer_len, "%s", value);
437 return TNC_RESULT_SUCCESS;
438 }
439 else
440 {
441 return TNC_RESULT_INVALID_PARAMETER;
442 }
443 }
444
445 METHOD(tnccs_manager_t, get_attribute, TNC_Result,
446 private_tnc_tnccs_manager_t *this, bool is_imc,
447 TNC_UInt32 imcv_id,
448 TNC_ConnectionID id,
449 TNC_AttributeID attribute_id,
450 TNC_UInt32 buffer_len,
451 TNC_BufferReference buffer,
452 TNC_UInt32 *value_len)
453 {
454 enumerator_t *enumerator;
455 tnccs_connection_entry_t *entry;
456 bool attribute_match = FALSE, entry_found = FALSE;
457
458 if (is_imc)
459 {
460 switch (attribute_id)
461 {
462 /* these attributes are unsupported */
463 case TNC_ATTRIBUTEID_SOHR:
464 case TNC_ATTRIBUTEID_SSOHR:
465 return TNC_RESULT_INVALID_PARAMETER;
466
467 /* these attributes are supported */
468 case TNC_ATTRIBUTEID_PRIMARY_IMC_ID:
469 attribute_match = TRUE;
470 break;
471
472 /* these attributes are yet to be matched */
473 default:
474 break;
475 }
476 }
477 else
478 {
479 switch (attribute_id)
480 {
481 /* these attributes are unsupported or invalid */
482 case TNC_ATTRIBUTEID_REASON_STRING:
483 case TNC_ATTRIBUTEID_REASON_LANGUAGE:
484 case TNC_ATTRIBUTEID_SOH:
485 case TNC_ATTRIBUTEID_SSOH:
486 return TNC_RESULT_INVALID_PARAMETER;
487
488 /* these attributes are supported */
489 case TNC_ATTRIBUTEID_PRIMARY_IMV_ID:
490 attribute_match = TRUE;
491 break;
492
493 /* these attributes are yet to be matched */
494 default:
495 break;
496 }
497 }
498
499 if (!attribute_match)
500 {
501 switch (attribute_id)
502 {
503 /* these attributes are supported */
504 case TNC_ATTRIBUTEID_PREFERRED_LANGUAGE:
505 case TNC_ATTRIBUTEID_MAX_ROUND_TRIPS:
506 case TNC_ATTRIBUTEID_MAX_MESSAGE_SIZE:
507 case TNC_ATTRIBUTEID_HAS_LONG_TYPES:
508 case TNC_ATTRIBUTEID_HAS_EXCLUSIVE:
509 case TNC_ATTRIBUTEID_HAS_SOH:
510 case TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL:
511 case TNC_ATTRIBUTEID_IFTNCCS_VERSION:
512 case TNC_ATTRIBUTEID_IFT_PROTOCOL:
513 case TNC_ATTRIBUTEID_IFT_VERSION:
514 break;
515
516 /* these attributes are unsupported or unknown */
517 case TNC_ATTRIBUTEID_DHPN:
518 case TNC_ATTRIBUTEID_TLS_UNIQUE:
519 default:
520 return TNC_RESULT_INVALID_PARAMETER;
521 }
522 }
523
524 /* attributes specific to the TNCC or TNCS are unsupported */
525 if (id == TNC_CONNECTIONID_ANY)
526 {
527 return TNC_RESULT_INVALID_PARAMETER;
528 }
529
530 this->connection_lock->read_lock(this->connection_lock);
531 enumerator = this->connections->create_enumerator(this->connections);
532 while (enumerator->enumerate(enumerator, &entry))
533 {
534 if (id == entry->id)
535 {
536 entry_found = TRUE;
537 break;
538 }
539 }
540 enumerator->destroy(enumerator);
541 this->connection_lock->unlock(this->connection_lock);
542
543 if (!entry_found)
544 {
545 return TNC_RESULT_INVALID_PARAMETER;
546 }
547
548 switch (attribute_id)
549 {
550 case TNC_ATTRIBUTEID_PREFERRED_LANGUAGE:
551 {
552 recommendations_t *recs;
553 chunk_t pref_lang;
554
555 recs = entry->recs;
556 if (!recs)
557 {
558 return TNC_RESULT_INVALID_PARAMETER;
559 }
560 pref_lang = recs->get_preferred_language(recs);
561 if (pref_lang.len == 0)
562 {
563 return TNC_RESULT_INVALID_PARAMETER;
564 }
565 *value_len = pref_lang.len;
566 if (buffer && buffer_len >= pref_lang.len)
567 {
568 memcpy(buffer, pref_lang.ptr, pref_lang.len);
569 }
570 return TNC_RESULT_SUCCESS;
571 }
572 case TNC_ATTRIBUTEID_MAX_ROUND_TRIPS:
573 return uint_attribute(buffer_len, buffer, value_len,
574 0xffffffff);
575 case TNC_ATTRIBUTEID_MAX_MESSAGE_SIZE:
576 return uint_attribute(buffer_len, buffer, value_len,
577 entry->max_msg_len);
578 case TNC_ATTRIBUTEID_HAS_LONG_TYPES:
579 case TNC_ATTRIBUTEID_HAS_EXCLUSIVE:
580 return bool_attribute(buffer_len, buffer, value_len,
581 entry->type == TNCCS_2_0);
582 case TNC_ATTRIBUTEID_HAS_SOH:
583 return bool_attribute(buffer_len, buffer, value_len,
584 entry->type == TNCCS_SOH);
585 case TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL:
586 {
587 char *protocol;
588
589 switch (entry->type)
590 {
591 case TNCCS_1_1:
592 case TNCCS_2_0:
593 protocol = "IF-TNCCS";
594 break;
595 case TNCCS_SOH:
596 protocol = "IF-TNCCS-SOH";
597 break;
598 default:
599 return TNC_RESULT_INVALID_PARAMETER;
600 }
601 return str_attribute(buffer_len, buffer, value_len, protocol);
602 }
603 case TNC_ATTRIBUTEID_IFTNCCS_VERSION:
604 {
605 char *version;
606
607 switch (entry->type)
608 {
609 case TNCCS_1_1:
610 version = "1.1";
611 break;
612 case TNCCS_2_0:
613 version = "2.0";
614 break;
615 case TNCCS_SOH:
616 version = "1.0";
617 break;
618 default:
619 return TNC_RESULT_INVALID_PARAMETER;
620 }
621 return str_attribute(buffer_len, buffer, value_len, version);
622 }
623 case TNC_ATTRIBUTEID_IFT_PROTOCOL:
624 return str_attribute(buffer_len, buffer, value_len,
625 "IF-T for Tunneled EAP");
626 case TNC_ATTRIBUTEID_IFT_VERSION:
627 return str_attribute(buffer_len, buffer, value_len, "1.1");
628 default:
629 return TNC_RESULT_INVALID_PARAMETER;
630 }
631 }
632
633 METHOD(tnccs_manager_t, set_attribute, TNC_Result,
634 private_tnc_tnccs_manager_t *this, bool is_imc,
635 TNC_UInt32 imcv_id,
636 TNC_ConnectionID id,
637 TNC_AttributeID attribute_id,
638 TNC_UInt32 buffer_len,
639 TNC_BufferReference buffer)
640 {
641 enumerator_t *enumerator;
642 tnccs_connection_entry_t *entry;
643 recommendations_t *recs = NULL;
644
645 if (is_imc || id == TNC_CONNECTIONID_ANY ||
646 (attribute_id != TNC_ATTRIBUTEID_REASON_STRING &&
647 attribute_id != TNC_ATTRIBUTEID_REASON_LANGUAGE))
648 {
649 return TNC_RESULT_INVALID_PARAMETER;
650 }
651
652 this->connection_lock->read_lock(this->connection_lock);
653 enumerator = this->connections->create_enumerator(this->connections);
654 while (enumerator->enumerate(enumerator, &entry))
655 {
656 if (id == entry->id)
657 {
658 recs = entry->recs;
659 break;
660 }
661 }
662 enumerator->destroy(enumerator);
663 this->connection_lock->unlock(this->connection_lock);
664
665 if (recs)
666 {
667 chunk_t attribute = { buffer, buffer_len };
668
669 if (attribute_id == TNC_ATTRIBUTEID_REASON_STRING)
670 {
671 return recs->set_reason_string(recs, imcv_id, attribute);
672 }
673 else
674 {
675 return recs->set_reason_language(recs, imcv_id, attribute);
676 }
677 }
678 return TNC_RESULT_INVALID_PARAMETER;
679 }
680
681 METHOD(tnccs_manager_t, destroy, void,
682 private_tnc_tnccs_manager_t *this)
683 {
684 this->protocols->destroy_function(this->protocols, free);
685 this->protocol_lock->destroy(this->protocol_lock);
686 this->connections->destroy_function(this->connections, free);
687 this->connection_lock->destroy(this->connection_lock);
688 free(this);
689 }
690
691 /*
692 * See header
693 */
694 tnccs_manager_t *tnc_tnccs_manager_create()
695 {
696 private_tnc_tnccs_manager_t *this;
697
698 INIT(this,
699 .public = {
700 .add_method = _add_method,
701 .remove_method = _remove_method,
702 .create_instance = _create_instance,
703 .create_connection = _create_connection,
704 .remove_connection = _remove_connection,
705 .request_handshake_retry = _request_handshake_retry,
706 .send_message = _send_message,
707 .provide_recommendation = _provide_recommendation,
708 .get_attribute = _get_attribute,
709 .set_attribute = _set_attribute,
710 .destroy = _destroy,
711 },
712 .protocols = linked_list_create(),
713 .connections = linked_list_create(),
714 .protocol_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
715 .connection_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
716 );
717
718 return &this->public;
719 }
720