2 * Copyright (C) 2011-2012 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
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>.
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
17 #include "imv_agent.h"
18 #include "ietf/ietf_attr_assess_result.h"
20 #include <tncif_names.h>
22 #include <utils/debug.h>
23 #include <threading/rwlock.h>
25 typedef struct private_imv_agent_t private_imv_agent_t
;
28 * Private data of an imv_agent_t object.
30 struct private_imv_agent_t
{
33 * Public members of imv_agent_t
43 * message types registered by IMV
45 pen_type_t
*supported_types
;
48 * number of message types registered by IMV
53 * ID of IMV as assigned by TNCS
58 * List of additional IMV IDs assigned by TNCS
60 linked_list_t
*additional_ids
;
63 * list of TNCS connection entries
65 linked_list_t
*connections
;
68 * rwlock to lock TNCS connection entries
70 rwlock_t
*connection_lock
;
73 * Inform a TNCS about the set of message types the IMV is able to receive
75 * @param imv_id IMV ID assigned by TNCS
76 * @param supported_types list of supported message types
77 * @param type_count number of list elements
78 * @return TNC result code
80 TNC_Result (*report_message_types
)(TNC_IMVID imv_id
,
81 TNC_MessageTypeList supported_types
,
82 TNC_UInt32 type_count
);
85 * Inform a TNCS about the set of message types the IMV is able to receive
87 * @param imv_id IMV ID assigned by TNCS
88 * @param supported_vids list of supported message vendor IDs
89 * @param supported_subtypes list of supported message subtypes
90 * @param type_count number of list elements
91 * @return TNC result code
93 TNC_Result (*report_message_types_long
)(TNC_IMVID imv_id
,
94 TNC_VendorIDList supported_vids
,
95 TNC_MessageSubtypeList supported_subtypes
,
96 TNC_UInt32 type_count
);
99 * Deliver IMV Action Recommendation and IMV Evaluation Results to the TNCS
101 * @param imv_id IMV ID assigned by TNCS
102 # @param connection_id network connection ID assigned by TNCS
103 * @param rec IMV action recommendation
104 * @param eval IMV evaluation result
105 * @return TNC result code
107 TNC_Result (*provide_recommendation
)(TNC_IMVID imv_id
,
108 TNC_ConnectionID connection_id
,
109 TNC_IMV_Action_Recommendation rec
,
110 TNC_IMV_Evaluation_Result eval
);
113 * Get the value of an attribute associated with a connection
114 * or with the TNCS as a whole.
116 * @param imv_id IMV ID assigned by TNCS
117 * @param connection_id network connection ID assigned by TNCS
118 * @param attribute_id attribute ID
119 * @param buffer_len length of buffer in bytes
120 * @param buffer buffer
121 * @param out_value_len size in bytes of attribute stored in buffer
122 * @return TNC result code
124 TNC_Result (*get_attribute
)(TNC_IMVID imv_id
,
125 TNC_ConnectionID connection_id
,
126 TNC_AttributeID attribute_id
,
127 TNC_UInt32 buffer_len
,
128 TNC_BufferReference buffer
,
129 TNC_UInt32
*out_value_len
);
132 * Set the value of an attribute associated with a connection
133 * or with the TNCS as a whole.
135 * @param imv_id IMV ID assigned by TNCS
136 * @param connection_id network connection ID assigned by TNCS
137 * @param attribute_id attribute ID
138 * @param buffer_len length of buffer in bytes
139 * @param buffer buffer
140 * @return TNC result code
142 TNC_Result (*set_attribute
)(TNC_IMVID imv_id
,
143 TNC_ConnectionID connection_id
,
144 TNC_AttributeID attribute_id
,
145 TNC_UInt32 buffer_len
,
146 TNC_BufferReference buffer
);
149 * Reserve an additional IMV ID
151 * @param imv_id primary IMV ID assigned by TNCS
152 * @param out_imv_id additional IMV ID assigned by TNCS
153 * @return TNC result code
155 TNC_Result (*reserve_additional_id
)(TNC_IMVID imv_id
,
156 TNC_UInt32
*out_imv_id
);
160 METHOD(imv_agent_t
, bind_functions
, TNC_Result
,
161 private_imv_agent_t
*this, TNC_TNCS_BindFunctionPointer bind_function
)
165 DBG1(DBG_IMV
, "TNC server failed to provide bind function");
166 return TNC_RESULT_INVALID_PARAMETER
;
168 if (bind_function(this->id
, "TNC_TNCS_ReportMessageTypes",
169 (void**)&this->report_message_types
) != TNC_RESULT_SUCCESS
)
171 this->report_message_types
= NULL
;
173 if (bind_function(this->id
, "TNC_TNCS_ReportMessageTypesLong",
174 (void**)&this->report_message_types_long
) != TNC_RESULT_SUCCESS
)
176 this->report_message_types_long
= NULL
;
178 if (bind_function(this->id
, "TNC_TNCS_RequestHandshakeRetry",
179 (void**)&this->public.request_handshake_retry
) != TNC_RESULT_SUCCESS
)
181 this->public.request_handshake_retry
= NULL
;
183 if (bind_function(this->id
, "TNC_TNCS_SendMessage",
184 (void**)&this->public.send_message
) != TNC_RESULT_SUCCESS
)
186 this->public.send_message
= NULL
;
188 if (bind_function(this->id
, "TNC_TNCS_SendMessageLong",
189 (void**)&this->public.send_message_long
) != TNC_RESULT_SUCCESS
)
191 this->public.send_message_long
= NULL
;
193 if (bind_function(this->id
, "TNC_TNCS_ProvideRecommendation",
194 (void**)&this->provide_recommendation
) != TNC_RESULT_SUCCESS
)
196 this->provide_recommendation
= NULL
;
198 if (bind_function(this->id
, "TNC_TNCS_GetAttribute",
199 (void**)&this->get_attribute
) != TNC_RESULT_SUCCESS
)
201 this->get_attribute
= NULL
;
203 if (bind_function(this->id
, "TNC_TNCS_SetAttribute",
204 (void**)&this->set_attribute
) != TNC_RESULT_SUCCESS
)
206 this->set_attribute
= NULL
;
208 if (bind_function(this->id
, "TNC_TNCC_ReserveAdditionalIMVID",
209 (void**)&this->reserve_additional_id
) != TNC_RESULT_SUCCESS
)
211 this->reserve_additional_id
= NULL
;
213 DBG2(DBG_IMV
, "IMV %u \"%s\" provided with bind function",
214 this->id
, this->name
);
216 if (this->report_message_types_long
)
218 TNC_VendorIDList vendor_id_list
;
219 TNC_MessageSubtypeList subtype_list
;
222 vendor_id_list
= malloc(this->type_count
* sizeof(TNC_UInt32
));
223 subtype_list
= malloc(this->type_count
* sizeof(TNC_UInt32
));
225 for (i
= 0; i
< this->type_count
; i
++)
227 vendor_id_list
[i
] = this->supported_types
[i
].vendor_id
;
228 subtype_list
[i
] = this->supported_types
[i
].type
;
230 this->report_message_types_long(this->id
, vendor_id_list
, subtype_list
,
232 free(vendor_id_list
);
235 else if (this->report_message_types
)
237 TNC_MessageTypeList type_list
;
240 type_list
= malloc(this->type_count
* sizeof(TNC_UInt32
));
242 for (i
= 0; i
< this->type_count
; i
++)
244 type_list
[i
] = (this->supported_types
[i
].vendor_id
<< 8) |
245 (this->supported_types
[i
].type
& 0xff);
247 this->report_message_types(this->id
, type_list
, this->type_count
);
250 return TNC_RESULT_SUCCESS
;
254 * finds a connection state based on its Connection ID
256 static imv_state_t
* find_connection(private_imv_agent_t
*this,
259 enumerator_t
*enumerator
;
260 imv_state_t
*state
, *found
= NULL
;
262 this->connection_lock
->read_lock(this->connection_lock
);
263 enumerator
= this->connections
->create_enumerator(this->connections
);
264 while (enumerator
->enumerate(enumerator
, &state
))
266 if (id
== state
->get_connection_id(state
))
272 enumerator
->destroy(enumerator
);
273 this->connection_lock
->unlock(this->connection_lock
);
279 * delete a connection state with a given Connection ID
281 static bool delete_connection(private_imv_agent_t
*this, TNC_ConnectionID id
)
283 enumerator_t
*enumerator
;
287 this->connection_lock
->write_lock(this->connection_lock
);
288 enumerator
= this->connections
->create_enumerator(this->connections
);
289 while (enumerator
->enumerate(enumerator
, &state
))
291 if (id
== state
->get_connection_id(state
))
294 state
->destroy(state
);
295 this->connections
->remove_at(this->connections
, enumerator
);
299 enumerator
->destroy(enumerator
);
300 this->connection_lock
->unlock(this->connection_lock
);
306 * Read a boolean attribute
308 static bool get_bool_attribute(private_imv_agent_t
*this, TNC_ConnectionID id
,
309 TNC_AttributeID attribute_id
)
314 return this->get_attribute
&&
315 this->get_attribute(this->id
, id
, attribute_id
, 4, buf
, &len
) ==
316 TNC_RESULT_SUCCESS
&& len
== 1 && *buf
== 0x01;
320 * Read a string attribute
322 static char* get_str_attribute(private_imv_agent_t
*this, TNC_ConnectionID id
,
323 TNC_AttributeID attribute_id
)
328 if (this->get_attribute
&&
329 this->get_attribute(this->id
, id
, attribute_id
, BUF_LEN
, buf
, &len
) ==
330 TNC_RESULT_SUCCESS
&& len
<= BUF_LEN
)
338 * Read an UInt32 attribute
340 static u_int32_t
get_uint_attribute(private_imv_agent_t
*this, TNC_ConnectionID id
,
341 TNC_AttributeID attribute_id
)
346 if (this->get_attribute
&&
347 this->get_attribute(this->id
, id
, attribute_id
, 4, buf
, &len
) ==
348 TNC_RESULT_SUCCESS
&& len
== 4)
355 METHOD(imv_agent_t
, create_state
, TNC_Result
,
356 private_imv_agent_t
*this, imv_state_t
*state
)
358 TNC_ConnectionID conn_id
;
359 char *tnccs_p
= NULL
, *tnccs_v
= NULL
, *t_p
= NULL
, *t_v
= NULL
;
360 bool has_long
= FALSE
, has_excl
= FALSE
, has_soh
= FALSE
;
361 u_int32_t max_msg_len
;
363 conn_id
= state
->get_connection_id(state
);
364 if (find_connection(this, conn_id
))
366 DBG1(DBG_IMV
, "IMV %u \"%s\" already created a state for Connection ID %u",
367 this->id
, this->name
, conn_id
);
368 state
->destroy(state
);
369 return TNC_RESULT_OTHER
;
372 /* Get and display attributes from TNCS via IF-IMV */
373 has_long
= get_bool_attribute(this, conn_id
, TNC_ATTRIBUTEID_HAS_LONG_TYPES
);
374 has_excl
= get_bool_attribute(this, conn_id
, TNC_ATTRIBUTEID_HAS_EXCLUSIVE
);
375 has_soh
= get_bool_attribute(this, conn_id
, TNC_ATTRIBUTEID_HAS_SOH
);
376 tnccs_p
= get_str_attribute(this, conn_id
, TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL
);
377 tnccs_v
= get_str_attribute(this, conn_id
, TNC_ATTRIBUTEID_IFTNCCS_VERSION
);
378 t_p
= get_str_attribute(this, conn_id
, TNC_ATTRIBUTEID_IFT_PROTOCOL
);
379 t_v
= get_str_attribute(this, conn_id
, TNC_ATTRIBUTEID_IFT_VERSION
);
380 max_msg_len
= get_uint_attribute(this, conn_id
, TNC_ATTRIBUTEID_MAX_MESSAGE_SIZE
);
382 state
->set_flags(state
, has_long
, has_excl
);
383 state
->set_max_msg_len(state
, max_msg_len
);
385 DBG2(DBG_IMV
, "IMV %u \"%s\" created a state for %s %s Connection ID %u: "
386 "%slong %sexcl %ssoh", this->id
, this->name
,
387 tnccs_p ? tnccs_p
:"?", tnccs_v ? tnccs_v
:"?", conn_id
,
388 has_long ?
"+":"-", has_excl ?
"+":"-", has_soh ?
"+":"-");
389 DBG2(DBG_IMV
, " over %s %s with maximum PA-TNC message size of %u bytes",
390 t_p ? t_p
:"?", t_v ? t_v
:"?", max_msg_len
);
397 this->connection_lock
->write_lock(this->connection_lock
);
398 this->connections
->insert_last(this->connections
, state
);
399 this->connection_lock
->unlock(this->connection_lock
);
400 return TNC_RESULT_SUCCESS
;
403 METHOD(imv_agent_t
, delete_state
, TNC_Result
,
404 private_imv_agent_t
*this, TNC_ConnectionID connection_id
)
406 if (!delete_connection(this, connection_id
))
408 DBG1(DBG_IMV
, "IMV %u \"%s\" has no state for Connection ID %u",
409 this->id
, this->name
, connection_id
);
410 return TNC_RESULT_FATAL
;
412 DBG2(DBG_IMV
, "IMV %u \"%s\" deleted the state of Connection ID %u",
413 this->id
, this->name
, connection_id
);
414 return TNC_RESULT_SUCCESS
;
417 METHOD(imv_agent_t
, change_state
, TNC_Result
,
418 private_imv_agent_t
*this, TNC_ConnectionID connection_id
,
419 TNC_ConnectionState new_state
,
420 imv_state_t
**state_p
)
426 case TNC_CONNECTION_STATE_HANDSHAKE
:
427 case TNC_CONNECTION_STATE_ACCESS_ALLOWED
:
428 case TNC_CONNECTION_STATE_ACCESS_ISOLATED
:
429 case TNC_CONNECTION_STATE_ACCESS_NONE
:
430 state
= find_connection(this, connection_id
);
433 DBG1(DBG_IMV
, "IMV %u \"%s\" has no state for Connection ID %u",
434 this->id
, this->name
, connection_id
);
435 return TNC_RESULT_FATAL
;
437 state
->change_state(state
, new_state
);
438 DBG2(DBG_IMV
, "IMV %u \"%s\" changed state of Connection ID %u to '%N'",
439 this->id
, this->name
, connection_id
,
440 TNC_Connection_State_names
, new_state
);
446 case TNC_CONNECTION_STATE_CREATE
:
447 DBG1(DBG_IMV
, "state '%N' should be handled by create_state()",
448 TNC_Connection_State_names
, new_state
);
449 return TNC_RESULT_FATAL
;
450 case TNC_CONNECTION_STATE_DELETE
:
451 DBG1(DBG_IMV
, "state '%N' should be handled by delete_state()",
452 TNC_Connection_State_names
, new_state
);
453 return TNC_RESULT_FATAL
;
455 DBG1(DBG_IMV
, "IMV %u \"%s\" was notified of unknown state %u "
456 "for Connection ID %u",
457 this->id
, this->name
, new_state
, connection_id
);
458 return TNC_RESULT_INVALID_PARAMETER
;
460 return TNC_RESULT_SUCCESS
;
463 METHOD(imv_agent_t
, get_state
, bool,
464 private_imv_agent_t
*this, TNC_ConnectionID connection_id
,
467 *state
= find_connection(this, connection_id
);
470 DBG1(DBG_IMV
, "IMV %u \"%s\" has no state for Connection ID %u",
471 this->id
, this->name
, connection_id
);
477 METHOD(imv_agent_t
, get_name
, const char*,
478 private_imv_agent_t
*this)
483 METHOD(imv_agent_t
, get_id
, TNC_IMVID
,
484 private_imv_agent_t
*this)
489 METHOD(imv_agent_t
, reserve_additional_ids
, TNC_Result
,
490 private_imv_agent_t
*this, int count
)
496 if (!this->reserve_additional_id
)
498 DBG1(DBG_IMV
, "IMV %u \"%s\" did not detect the capability to reserve "
499 "additional IMV IDs from the TNCS", this->id
, this->name
);
500 return TNC_RESULT_ILLEGAL_OPERATION
;
504 result
= this->reserve_additional_id(this->id
, &id
);
505 if (result
!= TNC_RESULT_SUCCESS
)
507 DBG1(DBG_IMV
, "IMV %u \"%s\" failed to reserve %d additional IMV IDs",
508 this->id
, this->name
, count
);
513 /* store the scalar value in the pointer */
515 this->additional_ids
->insert_last(this->additional_ids
, pointer
);
516 DBG2(DBG_IMV
, "IMV %u \"%s\" reserved additional ID %u",
517 this->id
, this->name
, id
);
519 return TNC_RESULT_SUCCESS
;
522 METHOD(imv_agent_t
, count_additional_ids
, int,
523 private_imv_agent_t
*this)
525 return this->additional_ids
->get_count(this->additional_ids
);
528 METHOD(imv_agent_t
, create_id_enumerator
, enumerator_t
*,
529 private_imv_agent_t
*this)
531 return this->additional_ids
->create_enumerator(this->additional_ids
);
536 * implements enumerator_t
548 char lang_buf
[BUF_LEN
];
551 * position pointer into language buffer
555 } language_enumerator_t
;
558 * Implementation of language_enumerator.destroy.
560 static void language_enumerator_destroy(language_enumerator_t
*this)
566 * Implementation of language_enumerator.enumerate
568 static bool language_enumerator_enumerate(language_enumerator_t
*this, ...)
570 char *pos
, *cur_lang
, **lang
;
578 cur_lang
= this->lang_pos
;
579 pos
= strchr(this->lang_pos
, ',');
582 len
= pos
- this->lang_pos
;
583 this->lang_pos
+= len
+ 1,
584 this->lang_len
-= len
+ 1;
588 len
= this->lang_len
;
589 pos
= this->lang_pos
+ len
;
590 this->lang_pos
= NULL
;
594 /* remove preceding whitespace */
595 while (*cur_lang
== ' ' && len
--)
600 /* remove trailing whitespace */
601 while (len
&& *(--pos
) == ' ')
605 cur_lang
[len
] = '\0';
607 va_start(args
, this);
608 lang
= va_arg(args
, char**);
615 METHOD(imv_agent_t
, create_language_enumerator
, enumerator_t
*,
616 private_imv_agent_t
*this, imv_state_t
*state
)
618 language_enumerator_t
*e
;
620 /* Create a language enumerator instance */
621 e
= malloc_thing(language_enumerator_t
);
622 e
->public.enumerate
= (void*)language_enumerator_enumerate
;
623 e
->public.destroy
= (void*)language_enumerator_destroy
;
625 if (!this->get_attribute
||
626 !this->get_attribute(this->id
, state
->get_connection_id(state
),
627 TNC_ATTRIBUTEID_PREFERRED_LANGUAGE
, BUF_LEN
,
628 e
->lang_buf
, &e
->lang_len
) == TNC_RESULT_SUCCESS
||
629 e
->lang_len
>= BUF_LEN
)
633 e
->lang_buf
[e
->lang_len
] = '\0';
634 e
->lang_pos
= e
->lang_buf
;
636 return (enumerator_t
*)e
;
639 METHOD(imv_agent_t
, provide_recommendation
, TNC_Result
,
640 private_imv_agent_t
*this, imv_state_t
*state
)
642 TNC_IMV_Action_Recommendation rec
;
643 TNC_IMV_Evaluation_Result eval
;
644 TNC_ConnectionID connection_id
;
645 char *reason_string
, *reason_lang
;
648 state
->get_recommendation(state
, &rec
, &eval
);
649 connection_id
= state
->get_connection_id(state
);
651 /* send a reason string if action recommendation is not allow */
652 if (rec
!= TNC_IMV_ACTION_RECOMMENDATION_ALLOW
)
654 /* find a reason string for the preferred language and set it */
655 if (this->set_attribute
)
657 e
= create_language_enumerator(this, state
);
658 if (state
->get_reason_string(state
, e
, &reason_string
, &reason_lang
))
660 this->set_attribute(this->id
, connection_id
,
661 TNC_ATTRIBUTEID_REASON_STRING
,
662 strlen(reason_string
), reason_string
);
663 this->set_attribute(this->id
, connection_id
,
664 TNC_ATTRIBUTEID_REASON_LANGUAGE
,
665 strlen(reason_lang
), reason_lang
);
670 return this->provide_recommendation(this->id
, connection_id
, rec
, eval
);
673 METHOD(imv_agent_t
, destroy
, void,
674 private_imv_agent_t
*this)
676 DBG1(DBG_IMV
, "IMV %u \"%s\" terminated", this->id
, this->name
);
677 this->additional_ids
->destroy(this->additional_ids
);
678 this->connections
->destroy_offset(this->connections
,
679 offsetof(imv_state_t
, destroy
));
680 this->connection_lock
->destroy(this->connection_lock
);
683 /* decrease the reference count or terminate */
688 * Described in header.
690 imv_agent_t
*imv_agent_create(const char *name
,
691 pen_type_t
*supported_types
, u_int32_t type_count
,
692 TNC_IMVID id
, TNC_Version
*actual_version
)
694 private_imv_agent_t
*this;
696 /* initialize or increase the reference count */
704 .bind_functions
= _bind_functions
,
705 .create_state
= _create_state
,
706 .delete_state
= _delete_state
,
707 .change_state
= _change_state
,
708 .get_state
= _get_state
,
709 .get_name
= _get_name
,
711 .reserve_additional_ids
= _reserve_additional_ids
,
712 .count_additional_ids
= _count_additional_ids
,
713 .create_id_enumerator
= _create_id_enumerator
,
714 .create_language_enumerator
= _create_language_enumerator
,
715 .provide_recommendation
= _provide_recommendation
,
719 .supported_types
= supported_types
,
720 .type_count
= type_count
,
722 .additional_ids
= linked_list_create(),
723 .connections
= linked_list_create(),
724 .connection_lock
= rwlock_create(RWLOCK_TYPE_DEFAULT
),
727 *actual_version
= TNC_IFIMV_VERSION_1
;
728 DBG1(DBG_IMV
, "IMV %u \"%s\" initialized", this->id
, this->name
);
730 return &this->public;