moved management of additional IMC/IMV IDs to agent
[strongswan.git] / src / libimcv / imv / imv_agent.c
1 /*
2 * Copyright (C) 2011 Andreas Steffen, HSR Hochschule fuer Technik Rapperswil
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 */
14
15 #include "imcv.h"
16 #include "imv_agent.h"
17
18 #include <tncif_names.h>
19
20 #include <debug.h>
21 #include <utils/linked_list.h>
22 #include <threading/rwlock.h>
23
24 typedef struct private_imv_agent_t private_imv_agent_t;
25
26 /**
27 * Private data of an imv_agent_t object.
28 */
29 struct private_imv_agent_t {
30
31 /**
32 * Public members of imv_agent_t
33 */
34 imv_agent_t public;
35
36 /**
37 * name of IMV
38 */
39 const char *name;
40
41 /**
42 * message vendor ID of IMV
43 */
44 TNC_VendorID vendor_id;
45
46 /**
47 * message subtype of IMV
48 */
49 TNC_MessageSubtype subtype;
50
51 /**
52 * ID of IMV as assigned by TNCS
53 */
54 TNC_IMVID id;
55
56 /**
57 * List of additional IMV IDs assigned by TNCS
58 */
59 linked_list_t *additional_ids;
60
61 /**
62 * list of TNCS connection entries
63 */
64 linked_list_t *connections;
65
66 /**
67 * rwlock to lock TNCS connection entries
68 */
69 rwlock_t *connection_lock;
70
71 /**
72 * Inform a TNCS about the set of message types the IMV is able to receive
73 *
74 * @param imv_id IMV ID assigned by TNCS
75 * @param supported_types list of supported message types
76 * @param type_count number of list elements
77 * @return TNC result code
78 */
79 TNC_Result (*report_message_types)(TNC_IMVID imv_id,
80 TNC_MessageTypeList supported_types,
81 TNC_UInt32 type_count);
82
83 /**
84 * Inform a TNCS about the set of message types the IMV is able to receive
85 *
86 * @param imv_id IMV ID assigned by TNCS
87 * @param supported_vids list of supported message vendor IDs
88 * @param supported_subtypes list of supported message subtypes
89 * @param type_count number of list elements
90 * @return TNC result code
91 */
92 TNC_Result (*report_message_types_long)(TNC_IMVID imv_id,
93 TNC_VendorIDList supported_vids,
94 TNC_MessageSubtypeList supported_subtypes,
95 TNC_UInt32 type_count);
96
97 /**
98 * Call when an IMV-IMC message is to be sent
99 *
100 * @param imv_id IMV ID assigned by TNCS
101 * @param connection_id network connection ID assigned by TNCS
102 * @param msg message to send
103 * @param msg_len message length in bytes
104 * @param msg_type message type
105 * @return TNC result code
106 */
107 TNC_Result (*send_message)(TNC_IMVID imv_id,
108 TNC_ConnectionID connection_id,
109 TNC_BufferReference msg,
110 TNC_UInt32 msg_len,
111 TNC_MessageType msg_type);
112
113 /**
114 * Call when an IMV-IMC message is to be sent with long message types
115 *
116 * @param imv_id IMV ID assigned by TNCS
117 * @param connection_id network connection ID assigned by TNCS
118 * @param msg_flags message flags
119 * @param msg message to send
120 * @param msg_len message length in bytes
121 * @param msg_vid message vendor ID
122 * @param msg_subtype message subtype
123 * @param dst_imc_id destination IMC ID
124 * @return TNC result code
125 */
126 TNC_Result (*send_message_long)(TNC_IMVID imv_id,
127 TNC_ConnectionID connection_id,
128 TNC_UInt32 msg_flags,
129 TNC_BufferReference msg,
130 TNC_UInt32 msg_len,
131 TNC_VendorID msg_vid,
132 TNC_MessageSubtype msg_subtype,
133 TNC_UInt32 dst_imc_id);
134
135 /**
136 * Deliver IMV Action Recommendation and IMV Evaluation Results to the TNCS
137 *
138 * @param imv_id IMV ID assigned by TNCS
139 # @param connection_id network connection ID assigned by TNCS
140 * @param rec IMV action recommendation
141 * @param eval IMV evaluation result
142 * @return TNC result code
143 */
144 TNC_Result (*provide_recommendation)(TNC_IMVID imv_id,
145 TNC_ConnectionID connection_id,
146 TNC_IMV_Action_Recommendation rec,
147 TNC_IMV_Evaluation_Result eval);
148
149 /**
150 * Get the value of an attribute associated with a connection
151 * or with the TNCS as a whole.
152 *
153 * @param imv_id IMV ID assigned by TNCS
154 * @param connection_id network connection ID assigned by TNCS
155 * @param attribute_id attribute ID
156 * @param buffer_len length of buffer in bytes
157 * @param buffer buffer
158 * @param out_value_len size in bytes of attribute stored in buffer
159 * @return TNC result code
160 */
161 TNC_Result (*get_attribute)(TNC_IMVID imv_id,
162 TNC_ConnectionID connection_id,
163 TNC_AttributeID attribute_id,
164 TNC_UInt32 buffer_len,
165 TNC_BufferReference buffer,
166 TNC_UInt32 *out_value_len);
167
168 /**
169 * Set the value of an attribute associated with a connection
170 * or with the TNCS as a whole.
171 *
172 * @param imv_id IMV ID assigned by TNCS
173 * @param connection_id network connection ID assigned by TNCS
174 * @param attribute_id attribute ID
175 * @param buffer_len length of buffer in bytes
176 * @param buffer buffer
177 * @return TNC result code
178 */
179 TNC_Result (*set_attribute)(TNC_IMVID imv_id,
180 TNC_ConnectionID connection_id,
181 TNC_AttributeID attribute_id,
182 TNC_UInt32 buffer_len,
183 TNC_BufferReference buffer);
184
185 /**
186 * Reserve an additional IMV ID
187 *
188 * @param imv_id primary IMV ID assigned by TNCS
189 * @param out_imv_id additional IMV ID assigned by TNCS
190 * @return TNC result code
191 */
192 TNC_Result (*reserve_additional_id)(TNC_IMVID imv_id,
193 TNC_UInt32 *out_imv_id);
194
195 };
196
197 METHOD(imv_agent_t, bind_functions, TNC_Result,
198 private_imv_agent_t *this, TNC_TNCS_BindFunctionPointer bind_function)
199 {
200 if (!bind_function)
201 {
202 DBG1(DBG_IMV, "TNC server failed to provide bind function");
203 return TNC_RESULT_INVALID_PARAMETER;
204 }
205 if (bind_function(this->id, "TNC_TNCS_ReportMessageTypes",
206 (void**)&this->report_message_types) != TNC_RESULT_SUCCESS)
207 {
208 this->report_message_types = NULL;
209 }
210 if (bind_function(this->id, "TNC_TNCS_ReportMessageTypesLong",
211 (void**)&this->report_message_types_long) != TNC_RESULT_SUCCESS)
212 {
213 this->report_message_types_long = NULL;
214 }
215 if (bind_function(this->id, "TNC_TNCS_RequestHandshakeRetry",
216 (void**)&this->public.request_handshake_retry) != TNC_RESULT_SUCCESS)
217 {
218 this->public.request_handshake_retry = NULL;
219 }
220 if (bind_function(this->id, "TNC_TNCS_SendMessage",
221 (void**)&this->send_message) != TNC_RESULT_SUCCESS)
222 {
223 this->send_message = NULL;
224 }
225 if (bind_function(this->id, "TNC_TNCS_SendMessageLong",
226 (void**)&this->send_message_long) != TNC_RESULT_SUCCESS)
227 {
228 this->send_message_long = NULL;
229 }
230 if (bind_function(this->id, "TNC_TNCS_ProvideRecommendation",
231 (void**)&this->provide_recommendation) != TNC_RESULT_SUCCESS)
232 {
233 this->provide_recommendation = NULL;
234 }
235 if (bind_function(this->id, "TNC_TNCS_GetAttribute",
236 (void**)&this->get_attribute) != TNC_RESULT_SUCCESS)
237 {
238 this->get_attribute = NULL;
239 }
240 if (bind_function(this->id, "TNC_TNCS_SetAttribute",
241 (void**)&this->set_attribute) != TNC_RESULT_SUCCESS)
242 {
243 this->set_attribute = NULL;
244 }
245 if (bind_function(this->id, "TNC_TNCC_ReserveAdditionalIMVID",
246 (void**)&this->reserve_additional_id) != TNC_RESULT_SUCCESS)
247 {
248 this->reserve_additional_id = NULL;
249 }
250 DBG2(DBG_IMV, "IMV %u \"%s\" provided with bind function",
251 this->id, this->name);
252
253 if (this->report_message_types_long)
254 {
255 this->report_message_types_long(this->id, &this->vendor_id,
256 &this->subtype, 1);
257 }
258 else if (this->report_message_types &&
259 this->vendor_id <= TNC_VENDORID_ANY &&
260 this->subtype <= TNC_SUBTYPE_ANY)
261 {
262 TNC_MessageType type;
263
264 type = (this->vendor_id << 8) | this->subtype;
265 this->report_message_types(this->id, &type, 1);
266 }
267 return TNC_RESULT_SUCCESS;
268 }
269
270 /**
271 * finds a connection state based on its Connection ID
272 */
273 static imv_state_t* find_connection(private_imv_agent_t *this,
274 TNC_ConnectionID id)
275 {
276 enumerator_t *enumerator;
277 imv_state_t *state, *found = NULL;
278
279 this->connection_lock->read_lock(this->connection_lock);
280 enumerator = this->connections->create_enumerator(this->connections);
281 while (enumerator->enumerate(enumerator, &state))
282 {
283 if (id == state->get_connection_id(state))
284 {
285 found = state;
286 break;
287 }
288 }
289 enumerator->destroy(enumerator);
290 this->connection_lock->unlock(this->connection_lock);
291
292 return found;
293 }
294
295 /**
296 * delete a connection state with a given Connection ID
297 */
298 static bool delete_connection(private_imv_agent_t *this, TNC_ConnectionID id)
299 {
300 enumerator_t *enumerator;
301 imv_state_t *state;
302 bool found = FALSE;
303
304 this->connection_lock->write_lock(this->connection_lock);
305 enumerator = this->connections->create_enumerator(this->connections);
306 while (enumerator->enumerate(enumerator, &state))
307 {
308 if (id == state->get_connection_id(state))
309 {
310 found = TRUE;
311 state->destroy(state);
312 this->connections->remove_at(this->connections, enumerator);
313 break;
314 }
315 }
316 enumerator->destroy(enumerator);
317 this->connection_lock->unlock(this->connection_lock);
318
319 return found;
320 }
321
322 /**
323 * Read a boolean attribute
324 */
325 static bool get_bool_attribute(private_imv_agent_t *this, TNC_ConnectionID id,
326 TNC_AttributeID attribute_id)
327 {
328 TNC_UInt32 len;
329 char buf[4];
330
331 return this->get_attribute &&
332 this->get_attribute(this->id, id, attribute_id, 4, buf, &len) ==
333 TNC_RESULT_SUCCESS && len == 1 && *buf == 0x01;
334 }
335
336 /**
337 * Read a string attribute
338 */
339 static char* get_str_attribute(private_imv_agent_t *this, TNC_ConnectionID id,
340 TNC_AttributeID attribute_id)
341 {
342 TNC_UInt32 len;
343 char buf[BUF_LEN];
344
345 if (this->get_attribute &&
346 this->get_attribute(this->id, id, attribute_id, BUF_LEN, buf, &len) ==
347 TNC_RESULT_SUCCESS && len <= BUF_LEN)
348 {
349 return strdup(buf);
350 }
351 return NULL;
352 }
353
354 METHOD(imv_agent_t, create_state, TNC_Result,
355 private_imv_agent_t *this, imv_state_t *state)
356 {
357 TNC_ConnectionID conn_id;
358 char *tnccs_p = NULL, *tnccs_v = NULL, *t_p = NULL, *t_v = NULL;
359 bool has_long = FALSE, has_excl = FALSE, has_soh = FALSE;
360
361 conn_id = state->get_connection_id(state);
362 if (find_connection(this, conn_id))
363 {
364 DBG1(DBG_IMV, "IMV %u \"%s\" already created a state for Connection ID %u",
365 this->id, this->name, conn_id);
366 state->destroy(state);
367 return TNC_RESULT_OTHER;
368 }
369
370 /* Get and display attributes from TNCS via IF-IMV */
371 has_long = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_LONG_TYPES);
372 has_excl = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_EXCLUSIVE);
373 has_soh = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_SOH);
374 tnccs_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL);
375 tnccs_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_VERSION);
376 t_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_PROTOCOL);
377 t_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_VERSION);
378
379 state->set_flags(state, has_long, has_excl);
380
381 DBG2(DBG_IMV, "IMV %u \"%s\" created a state for Connection ID %u: "
382 "%s %s with %slong %sexcl %ssoh over %s %s",
383 this->id, this->name, conn_id, tnccs_p ? tnccs_p:"?",
384 tnccs_v ? tnccs_v:"?", has_long ? "+":"-", has_excl ? "+":"-",
385 has_soh ? "+":"-", t_p ? t_p:"?", t_v ? t_v :"?");
386 free(tnccs_p);
387 free(tnccs_v);
388 free(t_p);
389 free(t_v);
390
391 this->connection_lock->write_lock(this->connection_lock);
392 this->connections->insert_last(this->connections, state);
393 this->connection_lock->unlock(this->connection_lock);
394 return TNC_RESULT_SUCCESS;
395 }
396
397 METHOD(imv_agent_t, delete_state, TNC_Result,
398 private_imv_agent_t *this, TNC_ConnectionID connection_id)
399 {
400 if (!delete_connection(this, connection_id))
401 {
402 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
403 this->id, this->name, connection_id);
404 return TNC_RESULT_FATAL;
405 }
406 DBG2(DBG_IMV, "IMV %u \"%s\" deleted the state of Connection ID %u",
407 this->id, this->name, connection_id);
408 return TNC_RESULT_SUCCESS;
409 }
410
411 METHOD(imv_agent_t, change_state, TNC_Result,
412 private_imv_agent_t *this, TNC_ConnectionID connection_id,
413 TNC_ConnectionState new_state,
414 imv_state_t **state_p)
415 {
416 imv_state_t *state;
417
418 switch (new_state)
419 {
420 case TNC_CONNECTION_STATE_HANDSHAKE:
421 case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
422 case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
423 case TNC_CONNECTION_STATE_ACCESS_NONE:
424 state = find_connection(this, connection_id);
425 if (!state)
426 {
427 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
428 this->id, this->name, connection_id);
429 return TNC_RESULT_FATAL;
430 }
431 state->change_state(state, new_state);
432 DBG2(DBG_IMV, "IMV %u \"%s\" changed state of Connection ID %u to '%N'",
433 this->id, this->name, connection_id,
434 TNC_Connection_State_names, new_state);
435 if (state_p)
436 {
437 *state_p = state;
438 }
439 break;
440 case TNC_CONNECTION_STATE_CREATE:
441 DBG1(DBG_IMV, "state '%N' should be handled by create_state()",
442 TNC_Connection_State_names, new_state);
443 return TNC_RESULT_FATAL;
444 case TNC_CONNECTION_STATE_DELETE:
445 DBG1(DBG_IMV, "state '%N' should be handled by delete_state()",
446 TNC_Connection_State_names, new_state);
447 return TNC_RESULT_FATAL;
448 default:
449 DBG1(DBG_IMV, "IMV %u \"%s\" was notified of unknown state %u "
450 "for Connection ID %u",
451 this->id, this->name, new_state, connection_id);
452 return TNC_RESULT_INVALID_PARAMETER;
453 }
454 return TNC_RESULT_SUCCESS;
455 }
456
457 METHOD(imv_agent_t, get_state, bool,
458 private_imv_agent_t *this, TNC_ConnectionID connection_id,
459 imv_state_t **state)
460 {
461 *state = find_connection(this, connection_id);
462 if (!*state)
463 {
464 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
465 this->id, this->name, connection_id);
466 return FALSE;
467 }
468 return TRUE;
469 }
470
471 METHOD(imv_agent_t, send_message, TNC_Result,
472 private_imv_agent_t *this, TNC_ConnectionID connection_id, bool excl,
473 TNC_UInt32 src_imv_id, TNC_UInt32 dst_imc_id, chunk_t msg)
474 {
475 TNC_MessageType type;
476 TNC_UInt32 msg_flags;
477 imv_state_t *state;
478
479 state = find_connection(this, connection_id);
480 if (!state)
481 {
482 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
483 this->id, this->name, connection_id);
484 return TNC_RESULT_FATAL;
485 }
486
487 if (state->has_long(state) && this->send_message_long)
488 {
489 if (!src_imv_id)
490 {
491 src_imv_id = this->id;
492 }
493 msg_flags = excl ? TNC_MESSAGE_FLAGS_EXCLUSIVE : 0;
494
495 return this->send_message_long(src_imv_id, connection_id, msg_flags,
496 msg.ptr, msg.len, this->vendor_id,
497 this->subtype, dst_imc_id);
498 }
499 if (this->send_message)
500 {
501 type = (this->vendor_id << 8) | this->subtype;
502
503 return this->send_message(this->id, connection_id, msg.ptr, msg.len,
504 type);
505 }
506 return TNC_RESULT_FATAL;
507 }
508
509 METHOD(imv_agent_t, set_recommendation, TNC_Result,
510 private_imv_agent_t *this, TNC_ConnectionID connection_id,
511 TNC_IMV_Action_Recommendation rec,
512 TNC_IMV_Evaluation_Result eval)
513 {
514 imv_state_t *state;
515
516 state = find_connection(this, connection_id);
517 if (!state)
518 {
519 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
520 this->id, this->name, connection_id);
521 return TNC_RESULT_FATAL;
522 }
523
524 state->set_recommendation(state, rec, eval);
525 return this->provide_recommendation(this->id, connection_id, rec, eval);
526 }
527
528 METHOD(imv_agent_t, receive_message, TNC_Result,
529 private_imv_agent_t *this, imv_state_t *state, chunk_t msg,
530 TNC_VendorID msg_vid, TNC_MessageSubtype msg_subtype,
531 TNC_UInt32 src_imc_id, TNC_UInt32 dst_imv_id, pa_tnc_msg_t **pa_tnc_msg)
532 {
533 pa_tnc_msg_t *pa_msg, *error_msg;
534 pa_tnc_attr_t *error_attr;
535 enumerator_t *enumerator;
536 TNC_MessageType msg_type;
537 TNC_UInt32 msg_flags, src_imv_id, dst_imc_id;
538 TNC_ConnectionID connection_id;
539 TNC_Result result;
540
541 connection_id = state->get_connection_id(state);
542
543 if (state->has_long(state))
544 {
545 if (dst_imv_id != TNC_IMVID_ANY)
546 {
547 DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u "
548 "from IMC %u to IMV %u", this->id, this->name,
549 connection_id, src_imc_id, dst_imv_id);
550 }
551 else
552 {
553 DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u "
554 "from IMC %u", this->id, this->name, connection_id,
555 src_imc_id);
556 }
557 }
558 else
559 {
560 DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u",
561 this->id, this->name, connection_id);
562 }
563
564 *pa_tnc_msg = NULL;
565 pa_msg = pa_tnc_msg_create_from_data(msg);
566
567 switch (pa_msg->process(pa_msg))
568 {
569 case SUCCESS:
570 *pa_tnc_msg = pa_msg;
571 break;
572 case VERIFY_ERROR:
573 /* build error message */
574 error_msg = pa_tnc_msg_create();
575 enumerator = pa_msg->create_error_enumerator(pa_msg);
576 while (enumerator->enumerate(enumerator, &error_attr))
577 {
578 error_msg->add_attribute(error_msg,
579 error_attr->get_ref(error_attr));
580 }
581 enumerator->destroy(enumerator);
582 error_msg->build(error_msg);
583
584 /* send error message */
585 msg = error_msg->get_encoding(error_msg);
586
587 if (state->has_long(state) && this->send_message_long)
588 {
589 if (state->has_excl(state))
590 {
591 msg_flags = TNC_MESSAGE_FLAGS_EXCLUSIVE;
592 dst_imc_id = src_imc_id;
593 }
594 else
595 {
596 msg_flags = 0;
597 dst_imc_id = TNC_IMCID_ANY;
598 }
599 src_imv_id = (dst_imv_id == TNC_IMVID_ANY) ? this->id
600 : dst_imv_id;
601
602 result = this->send_message_long(src_imv_id, connection_id,
603 msg_flags, msg.ptr, msg.len, msg_vid,
604 msg_subtype, dst_imc_id);
605 }
606 else if (this->send_message)
607 {
608 msg_type = (msg_vid << 8) | msg_subtype;
609
610 result = this->send_message(this->id, connection_id,
611 msg.ptr, msg.len, msg_type);
612 }
613 else
614 {
615 result = TNC_RESULT_FATAL;
616 }
617
618 /* clean up */
619 error_msg->destroy(error_msg);
620 pa_msg->destroy(pa_msg);
621 return result;
622 case FAILED:
623 default:
624 pa_msg->destroy(pa_msg);
625 state->set_recommendation(state,
626 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
627 TNC_IMV_EVALUATION_RESULT_ERROR);
628 return this->provide_recommendation(this->id, connection_id,
629 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
630 TNC_IMV_EVALUATION_RESULT_ERROR);
631 }
632 return TNC_RESULT_SUCCESS;
633 }
634
635 METHOD(imv_agent_t, provide_recommendation, TNC_Result,
636 private_imv_agent_t *this, TNC_ConnectionID connection_id)
637 {
638 imv_state_t *state;
639 TNC_IMV_Action_Recommendation rec;
640 TNC_IMV_Evaluation_Result eval;
641 TNC_UInt32 lang_len;
642 char buf[BUF_LEN];
643 chunk_t pref_lang = { buf, 0 }, reason_string, reason_lang;
644
645 state = find_connection(this, connection_id);
646 if (!state)
647 {
648 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
649 this->id, this->name, connection_id);
650 return TNC_RESULT_FATAL;
651 }
652 state->get_recommendation(state, &rec, &eval);
653
654
655 /* send a reason string if action recommendation is not allow */
656 if (rec != TNC_IMV_ACTION_RECOMMENDATION_ALLOW)
657 {
658 /* check if there a preferred language has been requested */
659 if (this->get_attribute &&
660 this->get_attribute(this->id, connection_id,
661 TNC_ATTRIBUTEID_PREFERRED_LANGUAGE, BUF_LEN,
662 buf, &lang_len) == TNC_RESULT_SUCCESS &&
663 lang_len <= BUF_LEN)
664 {
665 pref_lang.len = lang_len;
666 DBG2(DBG_IMV, "preferred language is '%.*s'",
667 pref_lang.len, pref_lang.ptr);
668 }
669
670 /* find a reason string for the preferred or default language and set it */
671 if (this->set_attribute &&
672 state->get_reason_string(state, pref_lang, &reason_string,
673 &reason_lang))
674 {
675 this->set_attribute(this->id, connection_id,
676 TNC_ATTRIBUTEID_REASON_STRING,
677 reason_string.len, reason_string.ptr);
678 this->set_attribute(this->id, connection_id,
679 TNC_ATTRIBUTEID_REASON_LANGUAGE,
680 reason_lang.len, reason_lang.ptr);
681 }
682 }
683
684 return this->provide_recommendation(this->id, connection_id, rec, eval);
685 }
686
687 METHOD(imv_agent_t, reserve_additional_ids, TNC_Result,
688 private_imv_agent_t *this, int count)
689 {
690 TNC_Result result;
691 TNC_UInt32 id;
692 void *pointer;
693
694 if (!this->reserve_additional_id)
695 {
696 DBG1(DBG_IMV, "IMV %u \"%s\" did not detect the capability to reserve "
697 "additional IMV IDs from the TNCS", this->id, this->name);
698 return TNC_RESULT_ILLEGAL_OPERATION;
699 }
700 while (count > 0)
701 {
702 result = this->reserve_additional_id(this->id, &id);
703 if (result != TNC_RESULT_SUCCESS)
704 {
705 DBG1(DBG_IMV, "IMV %u \"%s\" failed to reserve %d additional IMV IDs",
706 this->id, this->name, count);
707 return result;
708 }
709 count--;
710
711 /* store the scalar value in the pointer */
712 pointer = (void*)id;
713 this->additional_ids->insert_last(this->additional_ids, pointer);
714 DBG2(DBG_IMV, "IMV %u \"%s\" reserved additional ID %u",
715 this->id, this->name, id);
716 }
717 return TNC_RESULT_SUCCESS;
718 }
719
720 METHOD(imv_agent_t, count_additional_ids, int,
721 private_imv_agent_t *this)
722 {
723 return this->additional_ids->get_count(this->additional_ids);
724 }
725
726 METHOD(imv_agent_t, create_id_enumerator, enumerator_t*,
727 private_imv_agent_t *this)
728 {
729 return this->additional_ids->create_enumerator(this->additional_ids);
730 }
731
732 METHOD(imv_agent_t, destroy, void,
733 private_imv_agent_t *this)
734 {
735 DBG1(DBG_IMV, "IMV %u \"%s\" terminated", this->id, this->name);
736 this->additional_ids->destroy(this->additional_ids);
737 this->connections->destroy_offset(this->connections,
738 offsetof(imv_state_t, destroy));
739 this->connection_lock->destroy(this->connection_lock);
740 free(this);
741
742 /* decrease the reference count or terminate */
743 libimcv_deinit();
744 }
745
746 /**
747 * Described in header.
748 */
749 imv_agent_t *imv_agent_create(const char *name,
750 pen_t vendor_id, u_int32_t subtype,
751 TNC_IMVID id, TNC_Version *actual_version)
752 {
753 private_imv_agent_t *this;
754
755 /* initialize or increase the reference count */
756 if (!libimcv_init())
757 {
758 return NULL;
759 }
760
761 INIT(this,
762 .public = {
763 .bind_functions = _bind_functions,
764 .create_state = _create_state,
765 .delete_state = _delete_state,
766 .change_state = _change_state,
767 .get_state = _get_state,
768 .send_message = _send_message,
769 .receive_message = _receive_message,
770 .set_recommendation = _set_recommendation,
771 .provide_recommendation = _provide_recommendation,
772 .reserve_additional_ids = _reserve_additional_ids,
773 .count_additional_ids = _count_additional_ids,
774 .create_id_enumerator = _create_id_enumerator,
775 .destroy = _destroy,
776 },
777 .name = name,
778 .vendor_id = vendor_id,
779 .subtype = subtype,
780 .id = id,
781 .additional_ids = linked_list_create(),
782 .connections = linked_list_create(),
783 .connection_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
784 );
785
786 *actual_version = TNC_IFIMV_VERSION_1;
787 DBG1(DBG_IMV, "IMV %u \"%s\" initialized", this->id, this->name);
788
789 return &this->public;
790 }
791
792