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