435c25a3c22a597de87680e52fb93750c214e72c
[strongswan.git] / src / libimcv / imv / imv_agent.c
1 /*
2 * Copyright (C) 2011-2013 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 u_int32_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 if (session)
300 {
301 imcv_db->remove_session(imcv_db, session);
302 }
303 state->destroy(state);
304 this->connections->remove_at(this->connections, enumerator);
305 break;
306 }
307 }
308 enumerator->destroy(enumerator);
309 this->connection_lock->unlock(this->connection_lock);
310
311 return found;
312 }
313
314 /**
315 * Read a boolean attribute
316 */
317 static bool get_bool_attribute(private_imv_agent_t *this, TNC_ConnectionID id,
318 TNC_AttributeID attribute_id)
319 {
320 TNC_UInt32 len;
321 char buf[4];
322
323 return this->get_attribute &&
324 this->get_attribute(this->id, id, attribute_id, 4, buf, &len) ==
325 TNC_RESULT_SUCCESS && len == 1 && *buf == 0x01;
326 }
327
328 /**
329 * Read a string attribute
330 */
331 static char* get_str_attribute(private_imv_agent_t *this, TNC_ConnectionID id,
332 TNC_AttributeID attribute_id)
333 {
334 TNC_UInt32 len;
335 char buf[BUF_LEN];
336
337 if (this->get_attribute &&
338 this->get_attribute(this->id, id, attribute_id, BUF_LEN, buf, &len) ==
339 TNC_RESULT_SUCCESS && len <= BUF_LEN)
340 {
341 return strdup(buf);
342 }
343 return NULL;
344 }
345
346 /**
347 * Read an UInt32 attribute
348 */
349 static u_int32_t get_uint_attribute(private_imv_agent_t *this, TNC_ConnectionID id,
350 TNC_AttributeID attribute_id)
351 {
352 TNC_UInt32 len;
353 char buf[4];
354
355 if (this->get_attribute &&
356 this->get_attribute(this->id, id, attribute_id, 4, buf, &len) ==
357 TNC_RESULT_SUCCESS && len == 4)
358 {
359 return untoh32(buf);
360 }
361 return 0;
362 }
363
364 /**
365 * Read a TNC identity attribute
366 */
367 static linked_list_t* get_identity_attribute(private_imv_agent_t *this,
368 TNC_ConnectionID id,
369 TNC_AttributeID attribute_id)
370 {
371 TNC_UInt32 len;
372 char buf[2048];
373 u_int32_t count;
374 tncif_identity_t *tnc_id;
375 bio_reader_t *reader;
376 linked_list_t *list;
377
378 list = linked_list_create();
379
380 if (!this->get_attribute ||
381 this->get_attribute(this->id, id, attribute_id, sizeof(buf), buf, &len)
382 != TNC_RESULT_SUCCESS || len > sizeof(buf))
383 {
384 return list;
385 }
386
387 reader = bio_reader_create(chunk_create(buf, len));
388 if (!reader->read_uint32(reader, &count))
389 {
390 goto end;
391 }
392 while (count--)
393 {
394 tnc_id = tncif_identity_create_empty();
395 if (!tnc_id->process(tnc_id, reader))
396 {
397 tnc_id->destroy(tnc_id);
398 goto end;
399 }
400 list->insert_last(list, tnc_id);
401 }
402
403 end:
404 reader->destroy(reader);
405 return list;
406 }
407
408 METHOD(imv_agent_t, create_state, TNC_Result,
409 private_imv_agent_t *this, imv_state_t *state)
410 {
411 TNC_ConnectionID conn_id;
412 char *tnccs_p = NULL, *tnccs_v = NULL, *t_p = NULL, *t_v = NULL;
413 bool has_long = FALSE, has_excl = FALSE, has_soh = FALSE, first = TRUE;
414 linked_list_t *ar_identities;
415 enumerator_t *enumerator;
416 tncif_identity_t *tnc_id;
417 imv_session_t *session;
418 u_int32_t max_msg_len;
419 u_int32_t ar_id_type = TNC_ID_UNKNOWN;
420 chunk_t ar_id_value = chunk_empty;
421
422 conn_id = state->get_connection_id(state);
423 if (find_connection(this, conn_id))
424 {
425 DBG1(DBG_IMV, "IMV %u \"%s\" already created a state for Connection ID %u",
426 this->id, this->name, conn_id);
427 state->destroy(state);
428 return TNC_RESULT_OTHER;
429 }
430
431 /* Get and display attributes from TNCS via IF-IMV */
432 has_long = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_LONG_TYPES);
433 has_excl = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_EXCLUSIVE);
434 has_soh = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_SOH);
435 tnccs_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL);
436 tnccs_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_VERSION);
437 t_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_PROTOCOL);
438 t_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_VERSION);
439 max_msg_len = get_uint_attribute(this, conn_id, TNC_ATTRIBUTEID_MAX_MESSAGE_SIZE);
440 ar_identities = get_identity_attribute(this, conn_id, TNC_ATTRIBUTEID_AR_IDENTITIES);
441
442 state->set_flags(state, has_long, has_excl);
443 state->set_max_msg_len(state, max_msg_len);
444
445 DBG2(DBG_IMV, "IMV %u \"%s\" created a state for %s %s Connection ID %u: "
446 "%slong %sexcl %ssoh", this->id, this->name,
447 tnccs_p ? tnccs_p:"?", tnccs_v ? tnccs_v:"?", conn_id,
448 has_long ? "+":"-", has_excl ? "+":"-", has_soh ? "+":"-");
449 DBG2(DBG_IMV, " over %s %s with maximum PA-TNC message size of %u bytes",
450 t_p ? t_p:"?", t_v ? t_v :"?", max_msg_len);
451
452 enumerator = ar_identities->create_enumerator(ar_identities);
453 while (enumerator->enumerate(enumerator, &tnc_id))
454 {
455 pen_type_t id_type, subject_type, auth_type;
456 u_int32_t tcg_id_type, tcg_subject_type, tcg_auth_type;
457 chunk_t id_value;
458
459 id_type = tnc_id->get_identity_type(tnc_id);
460 id_value = tnc_id->get_identity_value(tnc_id);
461 subject_type = tnc_id->get_subject_type(tnc_id);
462 auth_type = tnc_id->get_auth_type(tnc_id);
463
464 tcg_id_type = (id_type.vendor_id == PEN_TCG) ?
465 id_type.type : TNC_ID_UNKNOWN;
466 tcg_subject_type = (subject_type.vendor_id == PEN_TCG) ?
467 subject_type.type : TNC_SUBJECT_UNKNOWN;
468 tcg_auth_type = (auth_type.vendor_id == PEN_TCG) ?
469 auth_type.type : TNC_AUTH_UNKNOWN;
470
471
472 DBG2(DBG_IMV, " %N AR identity '%.*s' authenticated by %N",
473 TNC_Subject_names, tcg_subject_type,
474 id_value.len, id_value.ptr,
475 TNC_Authentication_names, tcg_auth_type);
476
477 if (first)
478 {
479 ar_id_type = tcg_id_type;
480 ar_id_value = id_value;
481 state->set_ar_id(state, ar_id_type, ar_id_value);
482 first = FALSE;
483 }
484 }
485 enumerator->destroy(enumerator);
486
487 if (imcv_db)
488 {
489 session = imcv_db->add_session(imcv_db, conn_id, ar_id_type, ar_id_value);
490 if (session)
491 {
492 DBG2(DBG_IMV, " assigned session ID %d",
493 session->get_session_id(session));
494 state->set_session(state, session);
495 }
496 else
497 {
498 DBG1(DBG_IMV, " no session ID assigned");
499 }
500 }
501 ar_identities->destroy_offset(ar_identities,
502 offsetof(tncif_identity_t, destroy));
503 free(tnccs_p);
504 free(tnccs_v);
505 free(t_p);
506 free(t_v);
507
508 this->connection_lock->write_lock(this->connection_lock);
509 this->connections->insert_last(this->connections, state);
510 this->connection_lock->unlock(this->connection_lock);
511 return TNC_RESULT_SUCCESS;
512 }
513
514 METHOD(imv_agent_t, delete_state, TNC_Result,
515 private_imv_agent_t *this, TNC_ConnectionID connection_id)
516 {
517 if (!delete_connection(this, connection_id))
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 DBG2(DBG_IMV, "IMV %u \"%s\" deleted the state of Connection ID %u",
524 this->id, this->name, connection_id);
525 return TNC_RESULT_SUCCESS;
526 }
527
528 METHOD(imv_agent_t, change_state, TNC_Result,
529 private_imv_agent_t *this, TNC_ConnectionID connection_id,
530 TNC_ConnectionState new_state,
531 imv_state_t **state_p)
532 {
533 imv_state_t *state;
534
535 switch (new_state)
536 {
537 case TNC_CONNECTION_STATE_HANDSHAKE:
538 case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
539 case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
540 case TNC_CONNECTION_STATE_ACCESS_NONE:
541 state = find_connection(this, connection_id);
542 if (!state)
543 {
544 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
545 this->id, this->name, connection_id);
546 return TNC_RESULT_FATAL;
547 }
548 state->change_state(state, new_state);
549 DBG2(DBG_IMV, "IMV %u \"%s\" changed state of Connection ID %u to '%N'",
550 this->id, this->name, connection_id,
551 TNC_Connection_State_names, new_state);
552 if (state_p)
553 {
554 *state_p = state;
555 }
556 break;
557 case TNC_CONNECTION_STATE_CREATE:
558 DBG1(DBG_IMV, "state '%N' should be handled by create_state()",
559 TNC_Connection_State_names, new_state);
560 return TNC_RESULT_FATAL;
561 case TNC_CONNECTION_STATE_DELETE:
562 DBG1(DBG_IMV, "state '%N' should be handled by delete_state()",
563 TNC_Connection_State_names, new_state);
564 return TNC_RESULT_FATAL;
565 default:
566 DBG1(DBG_IMV, "IMV %u \"%s\" was notified of unknown state %u "
567 "for Connection ID %u",
568 this->id, this->name, new_state, connection_id);
569 return TNC_RESULT_INVALID_PARAMETER;
570 }
571 return TNC_RESULT_SUCCESS;
572 }
573
574 METHOD(imv_agent_t, get_state, bool,
575 private_imv_agent_t *this, TNC_ConnectionID connection_id,
576 imv_state_t **state)
577 {
578 *state = find_connection(this, connection_id);
579 if (!*state)
580 {
581 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
582 this->id, this->name, connection_id);
583 return FALSE;
584 }
585 return TRUE;
586 }
587
588 METHOD(imv_agent_t, get_name, const char*,
589 private_imv_agent_t *this)
590 {
591 return this->name;
592 }
593
594 METHOD(imv_agent_t, get_id, TNC_IMVID,
595 private_imv_agent_t *this)
596 {
597 return this->id;
598 }
599
600 METHOD(imv_agent_t, reserve_additional_ids, TNC_Result,
601 private_imv_agent_t *this, int count)
602 {
603 TNC_Result result;
604 TNC_UInt32 id;
605 void *pointer;
606
607 if (!this->reserve_additional_id)
608 {
609 DBG1(DBG_IMV, "IMV %u \"%s\" did not detect the capability to reserve "
610 "additional IMV IDs from the TNCS", this->id, this->name);
611 return TNC_RESULT_ILLEGAL_OPERATION;
612 }
613 while (count > 0)
614 {
615 result = this->reserve_additional_id(this->id, &id);
616 if (result != TNC_RESULT_SUCCESS)
617 {
618 DBG1(DBG_IMV, "IMV %u \"%s\" failed to reserve %d additional IMV IDs",
619 this->id, this->name, count);
620 return result;
621 }
622 count--;
623
624 /* store the scalar value in the pointer */
625 pointer = (void*)id;
626 this->additional_ids->insert_last(this->additional_ids, pointer);
627 DBG2(DBG_IMV, "IMV %u \"%s\" reserved additional ID %u",
628 this->id, this->name, id);
629 }
630 return TNC_RESULT_SUCCESS;
631 }
632
633 METHOD(imv_agent_t, count_additional_ids, int,
634 private_imv_agent_t *this)
635 {
636 return this->additional_ids->get_count(this->additional_ids);
637 }
638
639 METHOD(imv_agent_t, create_id_enumerator, enumerator_t*,
640 private_imv_agent_t *this)
641 {
642 return this->additional_ids->create_enumerator(this->additional_ids);
643 }
644
645 typedef struct {
646 /**
647 * implements enumerator_t
648 */
649 enumerator_t public;
650
651 /**
652 * language length
653 */
654 TNC_UInt32 lang_len;
655
656 /**
657 * language buffer
658 */
659 char lang_buf[BUF_LEN];
660
661 /**
662 * position pointer into language buffer
663 */
664 char *lang_pos;
665
666 } language_enumerator_t;
667
668 /**
669 * Implementation of language_enumerator.destroy.
670 */
671 static void language_enumerator_destroy(language_enumerator_t *this)
672 {
673 free(this);
674 }
675
676 /**
677 * Implementation of language_enumerator.enumerate
678 */
679 static bool language_enumerator_enumerate(language_enumerator_t *this, ...)
680 {
681 char *pos, *cur_lang, **lang;
682 TNC_UInt32 len;
683 va_list args;
684
685 if (!this->lang_len)
686 {
687 return FALSE;
688 }
689 cur_lang = this->lang_pos;
690 pos = strchr(this->lang_pos, ',');
691 if (pos)
692 {
693 len = pos - this->lang_pos;
694 this->lang_pos += len + 1,
695 this->lang_len -= len + 1;
696 }
697 else
698 {
699 len = this->lang_len;
700 pos = this->lang_pos + len;
701 this->lang_pos = NULL;
702 this->lang_len = 0;
703 }
704
705 /* remove preceding whitespace */
706 while (*cur_lang == ' ' && len--)
707 {
708 cur_lang++;
709 }
710
711 /* remove trailing whitespace */
712 while (len && *(--pos) == ' ')
713 {
714 len--;
715 }
716 cur_lang[len] = '\0';
717
718 va_start(args, this);
719 lang = va_arg(args, char**);
720 *lang = cur_lang;
721 va_end(args);
722
723 return TRUE;
724 }
725
726 METHOD(imv_agent_t, create_language_enumerator, enumerator_t*,
727 private_imv_agent_t *this, imv_state_t *state)
728 {
729 language_enumerator_t *e;
730
731 /* Create a language enumerator instance */
732 e = malloc_thing(language_enumerator_t);
733 e->public.enumerate = (void*)language_enumerator_enumerate;
734 e->public.destroy = (void*)language_enumerator_destroy;
735
736 if (!this->get_attribute ||
737 !this->get_attribute(this->id, state->get_connection_id(state),
738 TNC_ATTRIBUTEID_PREFERRED_LANGUAGE, BUF_LEN,
739 e->lang_buf, &e->lang_len) == TNC_RESULT_SUCCESS ||
740 e->lang_len >= BUF_LEN)
741 {
742 e->lang_len = 0;
743 }
744 e->lang_buf[e->lang_len] = '\0';
745 e->lang_pos = e->lang_buf;
746
747 return (enumerator_t*)e;
748 }
749
750 METHOD(imv_agent_t, provide_recommendation, TNC_Result,
751 private_imv_agent_t *this, imv_state_t *state)
752 {
753 TNC_IMV_Action_Recommendation rec;
754 TNC_IMV_Evaluation_Result eval;
755 TNC_ConnectionID connection_id;
756 chunk_t reason_string;
757 char *reason_lang;
758 enumerator_t *e;
759
760 state->get_recommendation(state, &rec, &eval);
761 connection_id = state->get_connection_id(state);
762
763 /* send a reason string if action recommendation is not allow */
764 if (rec != TNC_IMV_ACTION_RECOMMENDATION_ALLOW)
765 {
766 /* find a reason string for the preferred language and set it */
767 if (this->set_attribute)
768 {
769 e = create_language_enumerator(this, state);
770 if (state->get_reason_string(state, e, &reason_string, &reason_lang))
771 {
772 this->set_attribute(this->id, connection_id,
773 TNC_ATTRIBUTEID_REASON_STRING,
774 reason_string.len, reason_string.ptr);
775 this->set_attribute(this->id, connection_id,
776 TNC_ATTRIBUTEID_REASON_LANGUAGE,
777 strlen(reason_lang), reason_lang);
778 }
779 e->destroy(e);
780 }
781 }
782 return this->provide_recommendation(this->id, connection_id, rec, eval);
783 }
784
785 METHOD(imv_agent_t, destroy, void,
786 private_imv_agent_t *this)
787 {
788 DBG1(DBG_IMV, "IMV %u \"%s\" terminated", this->id, this->name);
789 this->additional_ids->destroy(this->additional_ids);
790 this->connections->destroy_offset(this->connections,
791 offsetof(imv_state_t, destroy));
792 this->connection_lock->destroy(this->connection_lock);
793 free(this);
794
795 /* decrease the reference count or terminate */
796 libimcv_deinit();
797 }
798
799 /**
800 * Described in header.
801 */
802 imv_agent_t *imv_agent_create(const char *name,
803 pen_type_t *supported_types, u_int32_t type_count,
804 TNC_IMVID id, TNC_Version *actual_version)
805 {
806 private_imv_agent_t *this;
807
808 /* initialize or increase the reference count */
809 if (!libimcv_init(TRUE))
810 {
811 return NULL;
812 }
813
814 INIT(this,
815 .public = {
816 .bind_functions = _bind_functions,
817 .create_state = _create_state,
818 .delete_state = _delete_state,
819 .change_state = _change_state,
820 .get_state = _get_state,
821 .get_name = _get_name,
822 .get_id = _get_id,
823 .reserve_additional_ids = _reserve_additional_ids,
824 .count_additional_ids = _count_additional_ids,
825 .create_id_enumerator = _create_id_enumerator,
826 .create_language_enumerator = _create_language_enumerator,
827 .provide_recommendation = _provide_recommendation,
828 .destroy = _destroy,
829 },
830 .name = name,
831 .supported_types = supported_types,
832 .type_count = type_count,
833 .id = id,
834 .additional_ids = linked_list_create(),
835 .connections = linked_list_create(),
836 .connection_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
837 );
838
839 *actual_version = TNC_IFIMV_VERSION_1;
840 DBG1(DBG_IMV, "IMV %u \"%s\" initialized", this->id, this->name);
841
842 return &this->public;
843 }
844
845