Separated IMV session management from IMV policy database
[strongswan.git] / src / libimcv / imv / imv_agent.c
1 /*
2 * Copyright (C) 2011-2014 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 "imcv.h"
17 #include "imv_agent.h"
18 #include "imv_session.h"
19
20 #include "ietf/ietf_attr_assess_result.h"
21
22 #include <tncif_names.h>
23 #include <tncif_identity.h>
24
25 #include <utils/debug.h>
26 #include <collections/linked_list.h>
27 #include <bio/bio_reader.h>
28 #include <threading/rwlock.h>
29
30 typedef struct private_imv_agent_t private_imv_agent_t;
31
32 /**
33 * Private data of an imv_agent_t object.
34 */
35 struct private_imv_agent_t {
36
37 /**
38 * Public members of imv_agent_t
39 */
40 imv_agent_t public;
41
42 /**
43 * name of IMV
44 */
45 const char *name;
46
47 /**
48 * message types registered by IMV
49 */
50 pen_type_t *supported_types;
51
52 /**
53 * number of message types registered by IMV
54 */
55 uint32_t type_count;
56
57 /**
58 * ID of IMV as assigned by TNCS
59 */
60 TNC_IMVID id;
61
62 /**
63 * List of additional IMV IDs assigned by TNCS
64 */
65 linked_list_t *additional_ids;
66
67 /**
68 * list of TNCS connection entries
69 */
70 linked_list_t *connections;
71
72 /**
73 * rwlock to lock TNCS connection entries
74 */
75 rwlock_t *connection_lock;
76
77 /**
78 * Inform a TNCS about the set of message types the IMV is able to receive
79 *
80 * @param imv_id IMV ID assigned by TNCS
81 * @param supported_types list of supported message types
82 * @param type_count number of list elements
83 * @return TNC result code
84 */
85 TNC_Result (*report_message_types)(TNC_IMVID imv_id,
86 TNC_MessageTypeList supported_types,
87 TNC_UInt32 type_count);
88
89 /**
90 * Inform a TNCS about the set of message types the IMV is able to receive
91 *
92 * @param imv_id IMV ID assigned by TNCS
93 * @param supported_vids list of supported message vendor IDs
94 * @param supported_subtypes list of supported message subtypes
95 * @param type_count number of list elements
96 * @return TNC result code
97 */
98 TNC_Result (*report_message_types_long)(TNC_IMVID imv_id,
99 TNC_VendorIDList supported_vids,
100 TNC_MessageSubtypeList supported_subtypes,
101 TNC_UInt32 type_count);
102
103 /**
104 * Deliver IMV Action Recommendation and IMV Evaluation Results to the TNCS
105 *
106 * @param imv_id IMV ID assigned by TNCS
107 # @param connection_id network connection ID assigned by TNCS
108 * @param rec IMV action recommendation
109 * @param eval IMV evaluation result
110 * @return TNC result code
111 */
112 TNC_Result (*provide_recommendation)(TNC_IMVID imv_id,
113 TNC_ConnectionID connection_id,
114 TNC_IMV_Action_Recommendation rec,
115 TNC_IMV_Evaluation_Result eval);
116
117 /**
118 * Get the value of an attribute associated with a connection
119 * or with the TNCS as a whole.
120 *
121 * @param imv_id IMV ID assigned by TNCS
122 * @param connection_id network connection ID assigned by TNCS
123 * @param attribute_id attribute ID
124 * @param buffer_len length of buffer in bytes
125 * @param buffer buffer
126 * @param out_value_len size in bytes of attribute stored in buffer
127 * @return TNC result code
128 */
129 TNC_Result (*get_attribute)(TNC_IMVID imv_id,
130 TNC_ConnectionID connection_id,
131 TNC_AttributeID attribute_id,
132 TNC_UInt32 buffer_len,
133 TNC_BufferReference buffer,
134 TNC_UInt32 *out_value_len);
135
136 /**
137 * Set the value of an attribute associated with a connection
138 * or with the TNCS as a whole.
139 *
140 * @param imv_id IMV ID assigned by TNCS
141 * @param connection_id network connection ID assigned by TNCS
142 * @param attribute_id attribute ID
143 * @param buffer_len length of buffer in bytes
144 * @param buffer buffer
145 * @return TNC result code
146 */
147 TNC_Result (*set_attribute)(TNC_IMVID imv_id,
148 TNC_ConnectionID connection_id,
149 TNC_AttributeID attribute_id,
150 TNC_UInt32 buffer_len,
151 TNC_BufferReference buffer);
152
153 /**
154 * Reserve an additional IMV ID
155 *
156 * @param imv_id primary IMV ID assigned by TNCS
157 * @param out_imv_id additional IMV ID assigned by TNCS
158 * @return TNC result code
159 */
160 TNC_Result (*reserve_additional_id)(TNC_IMVID imv_id,
161 TNC_UInt32 *out_imv_id);
162
163 };
164
165 METHOD(imv_agent_t, bind_functions, TNC_Result,
166 private_imv_agent_t *this, TNC_TNCS_BindFunctionPointer bind_function)
167 {
168 if (!bind_function)
169 {
170 DBG1(DBG_IMV, "TNC server failed to provide bind function");
171 return TNC_RESULT_INVALID_PARAMETER;
172 }
173 if (bind_function(this->id, "TNC_TNCS_ReportMessageTypes",
174 (void**)&this->report_message_types) != TNC_RESULT_SUCCESS)
175 {
176 this->report_message_types = NULL;
177 }
178 if (bind_function(this->id, "TNC_TNCS_ReportMessageTypesLong",
179 (void**)&this->report_message_types_long) != TNC_RESULT_SUCCESS)
180 {
181 this->report_message_types_long = NULL;
182 }
183 if (bind_function(this->id, "TNC_TNCS_RequestHandshakeRetry",
184 (void**)&this->public.request_handshake_retry) != TNC_RESULT_SUCCESS)
185 {
186 this->public.request_handshake_retry = NULL;
187 }
188 if (bind_function(this->id, "TNC_TNCS_SendMessage",
189 (void**)&this->public.send_message) != TNC_RESULT_SUCCESS)
190 {
191 this->public.send_message = NULL;
192 }
193 if (bind_function(this->id, "TNC_TNCS_SendMessageLong",
194 (void**)&this->public.send_message_long) != TNC_RESULT_SUCCESS)
195 {
196 this->public.send_message_long = NULL;
197 }
198 if (bind_function(this->id, "TNC_TNCS_ProvideRecommendation",
199 (void**)&this->provide_recommendation) != TNC_RESULT_SUCCESS)
200 {
201 this->provide_recommendation = NULL;
202 }
203 if (bind_function(this->id, "TNC_TNCS_GetAttribute",
204 (void**)&this->get_attribute) != TNC_RESULT_SUCCESS)
205 {
206 this->get_attribute = NULL;
207 }
208 if (bind_function(this->id, "TNC_TNCS_SetAttribute",
209 (void**)&this->set_attribute) != TNC_RESULT_SUCCESS)
210 {
211 this->set_attribute = NULL;
212 }
213 if (bind_function(this->id, "TNC_TNCC_ReserveAdditionalIMVID",
214 (void**)&this->reserve_additional_id) != TNC_RESULT_SUCCESS)
215 {
216 this->reserve_additional_id = NULL;
217 }
218
219 if (this->report_message_types_long)
220 {
221 TNC_VendorIDList vendor_id_list;
222 TNC_MessageSubtypeList subtype_list;
223 int i;
224
225 vendor_id_list = malloc(this->type_count * sizeof(TNC_UInt32));
226 subtype_list = malloc(this->type_count * sizeof(TNC_UInt32));
227
228 for (i = 0; i < this->type_count; i++)
229 {
230 vendor_id_list[i] = this->supported_types[i].vendor_id;
231 subtype_list[i] = this->supported_types[i].type;
232 }
233 this->report_message_types_long(this->id, vendor_id_list, subtype_list,
234 this->type_count);
235 free(vendor_id_list);
236 free(subtype_list);
237 }
238 else if (this->report_message_types)
239 {
240 TNC_MessageTypeList type_list;
241 int i;
242
243 type_list = malloc(this->type_count * sizeof(TNC_UInt32));
244
245 for (i = 0; i < this->type_count; i++)
246 {
247 type_list[i] = (this->supported_types[i].vendor_id << 8) |
248 (this->supported_types[i].type & 0xff);
249 }
250 this->report_message_types(this->id, type_list, this->type_count);
251 free(type_list);
252 }
253 return TNC_RESULT_SUCCESS;
254 }
255
256 /**
257 * finds a connection state based on its Connection ID
258 */
259 static imv_state_t* find_connection(private_imv_agent_t *this,
260 TNC_ConnectionID id)
261 {
262 enumerator_t *enumerator;
263 imv_state_t *state, *found = NULL;
264
265 this->connection_lock->read_lock(this->connection_lock);
266 enumerator = this->connections->create_enumerator(this->connections);
267 while (enumerator->enumerate(enumerator, &state))
268 {
269 if (id == state->get_connection_id(state))
270 {
271 found = state;
272 break;
273 }
274 }
275 enumerator->destroy(enumerator);
276 this->connection_lock->unlock(this->connection_lock);
277
278 return found;
279 }
280
281 /**
282 * delete a connection state with a given Connection ID
283 */
284 static bool delete_connection(private_imv_agent_t *this, TNC_ConnectionID id)
285 {
286 enumerator_t *enumerator;
287 imv_state_t *state;
288 imv_session_t *session;
289 bool found = FALSE;
290
291 this->connection_lock->write_lock(this->connection_lock);
292 enumerator = this->connections->create_enumerator(this->connections);
293 while (enumerator->enumerate(enumerator, &state))
294 {
295 if (id == state->get_connection_id(state))
296 {
297 found = TRUE;
298 session = state->get_session(state);
299 imcv_sessions->remove_session(imcv_sessions, session);
300 state->destroy(state);
301 this->connections->remove_at(this->connections, enumerator);
302 break;
303 }
304 }
305 enumerator->destroy(enumerator);
306 this->connection_lock->unlock(this->connection_lock);
307
308 return found;
309 }
310
311 /**
312 * Read a boolean attribute
313 */
314 static bool get_bool_attribute(private_imv_agent_t *this, TNC_ConnectionID id,
315 TNC_AttributeID attribute_id)
316 {
317 TNC_UInt32 len;
318 char buf[4];
319
320 return this->get_attribute &&
321 this->get_attribute(this->id, id, attribute_id, 4, buf, &len) ==
322 TNC_RESULT_SUCCESS && len == 1 && *buf == 0x01;
323 }
324
325 /**
326 * Read a string attribute
327 */
328 static char* get_str_attribute(private_imv_agent_t *this, TNC_ConnectionID id,
329 TNC_AttributeID attribute_id)
330 {
331 TNC_UInt32 len;
332 char buf[BUF_LEN];
333
334 if (this->get_attribute &&
335 this->get_attribute(this->id, id, attribute_id, BUF_LEN, buf, &len) ==
336 TNC_RESULT_SUCCESS && len <= BUF_LEN)
337 {
338 return strdup(buf);
339 }
340 return NULL;
341 }
342
343 /**
344 * Read an UInt32 attribute
345 */
346 static uint32_t get_uint_attribute(private_imv_agent_t *this, TNC_ConnectionID id,
347 TNC_AttributeID attribute_id)
348 {
349 TNC_UInt32 len;
350 char buf[4];
351
352 if (this->get_attribute &&
353 this->get_attribute(this->id, id, attribute_id, 4, buf, &len) ==
354 TNC_RESULT_SUCCESS && len == 4)
355 {
356 return untoh32(buf);
357 }
358 return 0;
359 }
360
361 /**
362 * Read a TNC identity attribute
363 */
364 static linked_list_t* get_identity_attribute(private_imv_agent_t *this,
365 TNC_ConnectionID id,
366 TNC_AttributeID attribute_id)
367 {
368 TNC_UInt32 len;
369 char buf[2048];
370 uint32_t count;
371 tncif_identity_t *tnc_id;
372 bio_reader_t *reader;
373 linked_list_t *list;
374
375 list = linked_list_create();
376
377 if (!this->get_attribute ||
378 this->get_attribute(this->id, id, attribute_id, sizeof(buf), buf, &len)
379 != TNC_RESULT_SUCCESS || len > sizeof(buf))
380 {
381 return list;
382 }
383
384 reader = bio_reader_create(chunk_create(buf, len));
385 if (!reader->read_uint32(reader, &count))
386 {
387 goto end;
388 }
389 while (count--)
390 {
391 tnc_id = tncif_identity_create_empty();
392 if (!tnc_id->process(tnc_id, reader))
393 {
394 tnc_id->destroy(tnc_id);
395 goto end;
396 }
397 list->insert_last(list, tnc_id);
398 }
399
400 end:
401 reader->destroy(reader);
402 return list;
403 }
404
405 METHOD(imv_agent_t, create_state, TNC_Result,
406 private_imv_agent_t *this, imv_state_t *state)
407 {
408 TNC_ConnectionID conn_id;
409 char *tnccs_p = NULL, *tnccs_v = NULL, *t_p = NULL, *t_v = NULL;
410 bool has_long = FALSE, has_excl = FALSE, has_soh = FALSE, first = TRUE;
411 linked_list_t *ar_identities;
412 enumerator_t *enumerator;
413 tncif_identity_t *tnc_id;
414 imv_session_t *session;
415 uint32_t max_msg_len;
416 uint32_t ar_id_type = TNC_ID_UNKNOWN;
417 chunk_t ar_id_value = chunk_empty;
418
419 conn_id = state->get_connection_id(state);
420 if (find_connection(this, conn_id))
421 {
422 DBG1(DBG_IMV, "IMV %u \"%s\" already created a state for Connection ID %u",
423 this->id, this->name, conn_id);
424 state->destroy(state);
425 return TNC_RESULT_OTHER;
426 }
427
428 /* Get and display attributes from TNCS via IF-IMV */
429 has_long = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_LONG_TYPES);
430 has_excl = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_EXCLUSIVE);
431 has_soh = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_SOH);
432 tnccs_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL);
433 tnccs_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_VERSION);
434 t_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_PROTOCOL);
435 t_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_VERSION);
436 max_msg_len = get_uint_attribute(this, conn_id, TNC_ATTRIBUTEID_MAX_MESSAGE_SIZE);
437 ar_identities = get_identity_attribute(this, conn_id, TNC_ATTRIBUTEID_AR_IDENTITIES);
438
439 state->set_flags(state, has_long, has_excl);
440 state->set_max_msg_len(state, max_msg_len);
441
442 DBG2(DBG_IMV, "IMV %u \"%s\" created a state for %s %s Connection ID %u: "
443 "%slong %sexcl %ssoh", this->id, this->name,
444 tnccs_p ? tnccs_p:"?", tnccs_v ? tnccs_v:"?", conn_id,
445 has_long ? "+":"-", has_excl ? "+":"-", has_soh ? "+":"-");
446 DBG2(DBG_IMV, " over %s %s with maximum PA-TNC message size of %u bytes",
447 t_p ? t_p:"?", t_v ? t_v :"?", max_msg_len);
448
449 enumerator = ar_identities->create_enumerator(ar_identities);
450 while (enumerator->enumerate(enumerator, &tnc_id))
451 {
452 pen_type_t id_type, subject_type, auth_type;
453 uint32_t tcg_id_type, tcg_subject_type, tcg_auth_type;
454 chunk_t id_value;
455
456 id_type = tnc_id->get_identity_type(tnc_id);
457 id_value = tnc_id->get_identity_value(tnc_id);
458 subject_type = tnc_id->get_subject_type(tnc_id);
459 auth_type = tnc_id->get_auth_type(tnc_id);
460
461 tcg_id_type = (id_type.vendor_id == PEN_TCG) ?
462 id_type.type : TNC_ID_UNKNOWN;
463 tcg_subject_type = (subject_type.vendor_id == PEN_TCG) ?
464 subject_type.type : TNC_SUBJECT_UNKNOWN;
465 tcg_auth_type = (auth_type.vendor_id == PEN_TCG) ?
466 auth_type.type : TNC_AUTH_UNKNOWN;
467
468
469 DBG2(DBG_IMV, " %N AR identity '%.*s' authenticated by %N",
470 TNC_Subject_names, tcg_subject_type,
471 id_value.len, id_value.ptr,
472 TNC_Authentication_names, tcg_auth_type);
473
474 /* keep the first access requestor ID */
475 if (first)
476 {
477 ar_id_type = tcg_id_type;
478 ar_id_value = id_value;
479 first = FALSE;
480 }
481 }
482 enumerator->destroy(enumerator);
483
484 session = imcv_sessions->add_session(imcv_sessions, conn_id,
485 ar_id_type, ar_id_value);
486 state->set_session(state, session);
487
488 /* clean up temporary variables */
489 ar_identities->destroy_offset(ar_identities,
490 offsetof(tncif_identity_t, destroy));
491 free(tnccs_p);
492 free(tnccs_v);
493 free(t_p);
494 free(t_v);
495
496 /* insert state in connection list */
497 this->connection_lock->write_lock(this->connection_lock);
498 this->connections->insert_last(this->connections, state);
499 this->connection_lock->unlock(this->connection_lock);
500
501 return TNC_RESULT_SUCCESS;
502 }
503
504 METHOD(imv_agent_t, delete_state, TNC_Result,
505 private_imv_agent_t *this, TNC_ConnectionID connection_id)
506 {
507 if (!delete_connection(this, connection_id))
508 {
509 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
510 this->id, this->name, connection_id);
511 return TNC_RESULT_FATAL;
512 }
513 DBG2(DBG_IMV, "IMV %u \"%s\" deleted the state of Connection ID %u",
514 this->id, this->name, connection_id);
515 return TNC_RESULT_SUCCESS;
516 }
517
518 METHOD(imv_agent_t, change_state, TNC_Result,
519 private_imv_agent_t *this, TNC_ConnectionID connection_id,
520 TNC_ConnectionState new_state,
521 imv_state_t **state_p)
522 {
523 imv_state_t *state;
524
525 switch (new_state)
526 {
527 case TNC_CONNECTION_STATE_HANDSHAKE:
528 case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
529 case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
530 case TNC_CONNECTION_STATE_ACCESS_NONE:
531 state = find_connection(this, connection_id);
532 if (!state)
533 {
534 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
535 this->id, this->name, connection_id);
536 return TNC_RESULT_FATAL;
537 }
538 state->change_state(state, new_state);
539 DBG2(DBG_IMV, "IMV %u \"%s\" changed state of Connection ID %u to '%N'",
540 this->id, this->name, connection_id,
541 TNC_Connection_State_names, new_state);
542 if (state_p)
543 {
544 *state_p = state;
545 }
546 break;
547 case TNC_CONNECTION_STATE_CREATE:
548 DBG1(DBG_IMV, "state '%N' should be handled by create_state()",
549 TNC_Connection_State_names, new_state);
550 return TNC_RESULT_FATAL;
551 case TNC_CONNECTION_STATE_DELETE:
552 DBG1(DBG_IMV, "state '%N' should be handled by delete_state()",
553 TNC_Connection_State_names, new_state);
554 return TNC_RESULT_FATAL;
555 default:
556 DBG1(DBG_IMV, "IMV %u \"%s\" was notified of unknown state %u "
557 "for Connection ID %u",
558 this->id, this->name, new_state, connection_id);
559 return TNC_RESULT_INVALID_PARAMETER;
560 }
561 return TNC_RESULT_SUCCESS;
562 }
563
564 METHOD(imv_agent_t, get_state, bool,
565 private_imv_agent_t *this, TNC_ConnectionID connection_id,
566 imv_state_t **state)
567 {
568 *state = find_connection(this, connection_id);
569 if (!*state)
570 {
571 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
572 this->id, this->name, connection_id);
573 return FALSE;
574 }
575 return TRUE;
576 }
577
578 METHOD(imv_agent_t, get_name, const char*,
579 private_imv_agent_t *this)
580 {
581 return this->name;
582 }
583
584 METHOD(imv_agent_t, get_id, TNC_IMVID,
585 private_imv_agent_t *this)
586 {
587 return this->id;
588 }
589
590 METHOD(imv_agent_t, reserve_additional_ids, TNC_Result,
591 private_imv_agent_t *this, int count)
592 {
593 TNC_Result result;
594 TNC_UInt32 id;
595 void *pointer;
596
597 if (!this->reserve_additional_id)
598 {
599 DBG1(DBG_IMV, "IMV %u \"%s\" did not detect the capability to reserve "
600 "additional IMV IDs from the TNCS", this->id, this->name);
601 return TNC_RESULT_ILLEGAL_OPERATION;
602 }
603 while (count > 0)
604 {
605 result = this->reserve_additional_id(this->id, &id);
606 if (result != TNC_RESULT_SUCCESS)
607 {
608 DBG1(DBG_IMV, "IMV %u \"%s\" failed to reserve %d additional IMV IDs",
609 this->id, this->name, count);
610 return result;
611 }
612 count--;
613
614 /* store the scalar value in the pointer */
615 pointer = (void*)id;
616 this->additional_ids->insert_last(this->additional_ids, pointer);
617 DBG2(DBG_IMV, "IMV %u \"%s\" reserved additional ID %u",
618 this->id, this->name, id);
619 }
620 return TNC_RESULT_SUCCESS;
621 }
622
623 METHOD(imv_agent_t, count_additional_ids, int,
624 private_imv_agent_t *this)
625 {
626 return this->additional_ids->get_count(this->additional_ids);
627 }
628
629 METHOD(imv_agent_t, create_id_enumerator, enumerator_t*,
630 private_imv_agent_t *this)
631 {
632 return this->additional_ids->create_enumerator(this->additional_ids);
633 }
634
635 typedef struct {
636 /**
637 * implements enumerator_t
638 */
639 enumerator_t public;
640
641 /**
642 * language length
643 */
644 TNC_UInt32 lang_len;
645
646 /**
647 * language buffer
648 */
649 char lang_buf[BUF_LEN];
650
651 /**
652 * position pointer into language buffer
653 */
654 char *lang_pos;
655
656 } language_enumerator_t;
657
658 /**
659 * Implementation of language_enumerator.destroy.
660 */
661 static void language_enumerator_destroy(language_enumerator_t *this)
662 {
663 free(this);
664 }
665
666 /**
667 * Implementation of language_enumerator.enumerate
668 */
669 static bool language_enumerator_enumerate(language_enumerator_t *this, ...)
670 {
671 char *pos, *cur_lang, **lang;
672 TNC_UInt32 len;
673 va_list args;
674
675 if (!this->lang_len)
676 {
677 return FALSE;
678 }
679 cur_lang = this->lang_pos;
680 pos = strchr(this->lang_pos, ',');
681 if (pos)
682 {
683 len = pos - this->lang_pos;
684 this->lang_pos += len + 1,
685 this->lang_len -= len + 1;
686 }
687 else
688 {
689 len = this->lang_len;
690 pos = this->lang_pos + len;
691 this->lang_pos = NULL;
692 this->lang_len = 0;
693 }
694
695 /* remove preceding whitespace */
696 while (*cur_lang == ' ' && len--)
697 {
698 cur_lang++;
699 }
700
701 /* remove trailing whitespace */
702 while (len && *(--pos) == ' ')
703 {
704 len--;
705 }
706 cur_lang[len] = '\0';
707
708 va_start(args, this);
709 lang = va_arg(args, char**);
710 *lang = cur_lang;
711 va_end(args);
712
713 return TRUE;
714 }
715
716 METHOD(imv_agent_t, create_language_enumerator, enumerator_t*,
717 private_imv_agent_t *this, imv_state_t *state)
718 {
719 language_enumerator_t *e;
720
721 /* Create a language enumerator instance */
722 e = malloc_thing(language_enumerator_t);
723 e->public.enumerate = (void*)language_enumerator_enumerate;
724 e->public.destroy = (void*)language_enumerator_destroy;
725
726 if (!this->get_attribute ||
727 !this->get_attribute(this->id, state->get_connection_id(state),
728 TNC_ATTRIBUTEID_PREFERRED_LANGUAGE, BUF_LEN,
729 e->lang_buf, &e->lang_len) == TNC_RESULT_SUCCESS ||
730 e->lang_len >= BUF_LEN)
731 {
732 e->lang_len = 0;
733 }
734 e->lang_buf[e->lang_len] = '\0';
735 e->lang_pos = e->lang_buf;
736
737 return (enumerator_t*)e;
738 }
739
740 METHOD(imv_agent_t, provide_recommendation, TNC_Result,
741 private_imv_agent_t *this, imv_state_t *state)
742 {
743 TNC_IMV_Action_Recommendation rec;
744 TNC_IMV_Evaluation_Result eval;
745 TNC_ConnectionID connection_id;
746 chunk_t reason_string;
747 char *reason_lang;
748 enumerator_t *e;
749
750 state->get_recommendation(state, &rec, &eval);
751 connection_id = state->get_connection_id(state);
752
753 /* send a reason string if action recommendation is not allow */
754 if (rec != TNC_IMV_ACTION_RECOMMENDATION_ALLOW)
755 {
756 /* find a reason string for the preferred language and set it */
757 if (this->set_attribute)
758 {
759 e = create_language_enumerator(this, state);
760 if (state->get_reason_string(state, e, &reason_string, &reason_lang))
761 {
762 this->set_attribute(this->id, connection_id,
763 TNC_ATTRIBUTEID_REASON_STRING,
764 reason_string.len, reason_string.ptr);
765 this->set_attribute(this->id, connection_id,
766 TNC_ATTRIBUTEID_REASON_LANGUAGE,
767 strlen(reason_lang), reason_lang);
768 }
769 e->destroy(e);
770 }
771 }
772 return this->provide_recommendation(this->id, connection_id, rec, eval);
773 }
774
775 METHOD(imv_agent_t, destroy, void,
776 private_imv_agent_t *this)
777 {
778 DBG1(DBG_IMV, "IMV %u \"%s\" terminated", this->id, this->name);
779 this->additional_ids->destroy(this->additional_ids);
780 this->connections->destroy_offset(this->connections,
781 offsetof(imv_state_t, destroy));
782 this->connection_lock->destroy(this->connection_lock);
783 free(this);
784
785 /* decrease the reference count or terminate */
786 libimcv_deinit();
787 }
788
789 /**
790 * Described in header.
791 */
792 imv_agent_t *imv_agent_create(const char *name,
793 pen_type_t *supported_types, uint32_t type_count,
794 TNC_IMVID id, TNC_Version *actual_version)
795 {
796 private_imv_agent_t *this;
797
798 /* initialize or increase the reference count */
799 if (!libimcv_init(TRUE))
800 {
801 return NULL;
802 }
803
804 INIT(this,
805 .public = {
806 .bind_functions = _bind_functions,
807 .create_state = _create_state,
808 .delete_state = _delete_state,
809 .change_state = _change_state,
810 .get_state = _get_state,
811 .get_name = _get_name,
812 .get_id = _get_id,
813 .reserve_additional_ids = _reserve_additional_ids,
814 .count_additional_ids = _count_additional_ids,
815 .create_id_enumerator = _create_id_enumerator,
816 .create_language_enumerator = _create_language_enumerator,
817 .provide_recommendation = _provide_recommendation,
818 .destroy = _destroy,
819 },
820 .name = name,
821 .supported_types = supported_types,
822 .type_count = type_count,
823 .id = id,
824 .additional_ids = linked_list_create(),
825 .connections = linked_list_create(),
826 .connection_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
827 );
828
829 *actual_version = TNC_IFIMV_VERSION_1;
830 DBG1(DBG_IMV, "IMV %u \"%s\" initialized", this->id, this->name);
831
832 return &this->public;
833 }
834
835