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