]> git.ipfire.org Git - people/ms/strongswan.git/blame - src/libimcv/imv/imv_agent.c
Make access requestor IP address available to TNC server
[people/ms/strongswan.git] / src / libimcv / imv / imv_agent.c
CommitLineData
510f37ab 1/*
00cd79b6 2 * Copyright (C) 2011-2015 Andreas Steffen
22e97e4f 3 * HSR Hochschule fuer Technik Rapperswil
510f37ab
AS
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
d4c8fe3c 16#include "imcv.h"
510f37ab 17#include "imv_agent.h"
a6266485
AS
18#include "imv_session.h"
19
6f93927b 20#include "ietf/ietf_attr_assess_result.h"
510f37ab 21
0eb23d7b 22#include <tncif_names.h>
bd1ee5bd 23#include <tncif_identity.h>
0eb23d7b 24
f05b4272 25#include <utils/debug.h>
bd1ee5bd
AS
26#include <collections/linked_list.h>
27#include <bio/bio_reader.h>
510f37ab
AS
28#include <threading/rwlock.h>
29
30typedef struct private_imv_agent_t private_imv_agent_t;
31
32/**
33 * Private data of an imv_agent_t object.
34 */
35struct 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 /**
5f085d7e 48 * message types registered by IMV
510f37ab 49 */
5f085d7e 50 pen_type_t *supported_types;
c87acaf5
AS
51
52 /**
5f085d7e 53 * number of message types registered by IMV
c87acaf5 54 */
4894bfa2 55 uint32_t type_count;
ea67a75b 56
510f37ab
AS
57 /**
58 * ID of IMV as assigned by TNCS
59 */
60 TNC_IMVID id;
61
cbf2ba54
AS
62 /**
63 * List of additional IMV IDs assigned by TNCS
64 */
65 linked_list_t *additional_ids;
66
38b5f527
AS
67 /**
68 * list of non-fatal unsupported PA-TNC attribute types
69 */
70 linked_list_t *non_fatal_attr_types;
71
510f37ab
AS
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 /**
c87acaf5 83 * Inform a TNCS about the set of message types the IMV is able to receive
510f37ab
AS
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
c87acaf5
AS
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
510f37ab
AS
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
44bd40a1
AS
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);
ac3331e1
AS
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
510f37ab
AS
168};
169
170METHOD(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 }
c87acaf5
AS
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 }
510f37ab
AS
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",
6a61b795 194 (void**)&this->public.send_message) != TNC_RESULT_SUCCESS)
510f37ab 195 {
6a61b795 196 this->public.send_message = NULL;
510f37ab 197 }
ac3331e1 198 if (bind_function(this->id, "TNC_TNCS_SendMessageLong",
6a61b795 199 (void**)&this->public.send_message_long) != TNC_RESULT_SUCCESS)
ac3331e1 200 {
6a61b795 201 this->public.send_message_long = NULL;
ac3331e1 202 }
510f37ab
AS
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",
44bd40a1 209 (void**)&this->get_attribute) != TNC_RESULT_SUCCESS)
510f37ab 210 {
44bd40a1 211 this->get_attribute = NULL;
510f37ab
AS
212 }
213 if (bind_function(this->id, "TNC_TNCS_SetAttribute",
44bd40a1 214 (void**)&this->set_attribute) != TNC_RESULT_SUCCESS)
510f37ab 215 {
44bd40a1 216 this->set_attribute = NULL;
510f37ab 217 }
ac3331e1
AS
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 }
510f37ab 223
c87acaf5 224 if (this->report_message_types_long)
510f37ab 225 {
5f085d7e
AS
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);
c87acaf5 242 }
5f085d7e 243 else if (this->report_message_types)
c87acaf5 244 {
5f085d7e
AS
245 TNC_MessageTypeList type_list;
246 int i;
247
248 type_list = malloc(this->type_count * sizeof(TNC_UInt32));
c87acaf5 249
5f085d7e
AS
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);
510f37ab
AS
257 }
258 return TNC_RESULT_SUCCESS;
259}
260
261/**
262 * finds a connection state based on its Connection ID
263 */
264static 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 */
289static bool delete_connection(private_imv_agent_t *this, TNC_ConnectionID id)
290{
291 enumerator_t *enumerator;
292 imv_state_t *state;
b1da8368 293 imv_session_t *session;
510f37ab
AS
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;
b1da8368 303 session = state->get_session(state);
4894bfa2 304 imcv_sessions->remove_session(imcv_sessions, session);
510f37ab
AS
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
1ab8dff7
AS
316/**
317 * Read a boolean attribute
318 */
319static 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 */
333static 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
968c83cd
AS
348/**
349 * Read an UInt32 attribute
350 */
4894bfa2 351static uint32_t get_uint_attribute(private_imv_agent_t *this, TNC_ConnectionID id,
968c83cd
AS
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
bd1ee5bd
AS
366/**
367 * Read a TNC identity attribute
368 */
369static 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];
4894bfa2 375 uint32_t count;
bd1ee5bd
AS
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
405end:
406 reader->destroy(reader);
407 return list;
408 }
409
510f37ab
AS
410METHOD(imv_agent_t, create_state, TNC_Result,
411 private_imv_agent_t *this, imv_state_t *state)
412{
1ab8dff7
AS
413 TNC_ConnectionID conn_id;
414 char *tnccs_p = NULL, *tnccs_v = NULL, *t_p = NULL, *t_v = NULL;
00cd79b6 415 bool has_long = FALSE, has_excl = FALSE, has_soh = FALSE;
bd1ee5bd 416 linked_list_t *ar_identities;
a6266485 417 imv_session_t *session;
4894bfa2 418 uint32_t max_msg_len;
510f37ab 419
1ab8dff7
AS
420 conn_id = state->get_connection_id(state);
421 if (find_connection(this, conn_id))
510f37ab
AS
422 {
423 DBG1(DBG_IMV, "IMV %u \"%s\" already created a state for Connection ID %u",
1ab8dff7 424 this->id, this->name, conn_id);
510f37ab
AS
425 state->destroy(state);
426 return TNC_RESULT_OTHER;
427 }
1ab8dff7
AS
428
429 /* Get and display attributes from TNCS via IF-IMV */
00cd79b6
AS
430 has_long = get_bool_attribute(this, conn_id,
431 TNC_ATTRIBUTEID_HAS_LONG_TYPES);
432 has_excl = get_bool_attribute(this, conn_id,
433 TNC_ATTRIBUTEID_HAS_EXCLUSIVE);
434 has_soh = get_bool_attribute(this, conn_id,
435 TNC_ATTRIBUTEID_HAS_SOH);
436 tnccs_p = get_str_attribute(this, conn_id,
437 TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL);
438 tnccs_v = get_str_attribute(this, conn_id,
439 TNC_ATTRIBUTEID_IFTNCCS_VERSION);
440 t_p = get_str_attribute(this, conn_id,
441 TNC_ATTRIBUTEID_IFT_PROTOCOL);
442 t_v = get_str_attribute(this, conn_id,
443 TNC_ATTRIBUTEID_IFT_VERSION);
444 max_msg_len = get_uint_attribute(this, conn_id,
445 TNC_ATTRIBUTEID_MAX_MESSAGE_SIZE);
446 ar_identities = get_identity_attribute(this, conn_id,
447 TNC_ATTRIBUTEID_AR_IDENTITIES);
1ab8dff7 448
e4e291d4 449 state->set_flags(state, has_long, has_excl);
968c83cd
AS
450 state->set_max_msg_len(state, max_msg_len);
451
452 DBG2(DBG_IMV, "IMV %u \"%s\" created a state for %s %s Connection ID %u: "
453 "%slong %sexcl %ssoh", this->id, this->name,
454 tnccs_p ? tnccs_p:"?", tnccs_v ? tnccs_v:"?", conn_id,
455 has_long ? "+":"-", has_excl ? "+":"-", has_soh ? "+":"-");
8ef43d87 456 DBG2(DBG_IMV, " over %s %s with maximum PA-TNC message size of %u bytes",
968c83cd 457 t_p ? t_p:"?", t_v ? t_v :"?", max_msg_len);
e4e291d4 458
00cd79b6 459 session = imcv_sessions->add_session(imcv_sessions, conn_id, ar_identities);
4894bfa2
AS
460 state->set_session(state, session);
461
1ab8dff7
AS
462 free(tnccs_p);
463 free(tnccs_v);
464 free(t_p);
465 free(t_v);
466
4894bfa2 467 /* insert state in connection list */
510f37ab
AS
468 this->connection_lock->write_lock(this->connection_lock);
469 this->connections->insert_last(this->connections, state);
470 this->connection_lock->unlock(this->connection_lock);
4894bfa2 471
510f37ab
AS
472 return TNC_RESULT_SUCCESS;
473}
474
475METHOD(imv_agent_t, delete_state, TNC_Result,
476 private_imv_agent_t *this, TNC_ConnectionID connection_id)
477{
478 if (!delete_connection(this, connection_id))
479 {
480 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
481 this->id, this->name, connection_id);
482 return TNC_RESULT_FATAL;
483 }
484 DBG2(DBG_IMV, "IMV %u \"%s\" deleted the state of Connection ID %u",
485 this->id, this->name, connection_id);
486 return TNC_RESULT_SUCCESS;
487}
488
489METHOD(imv_agent_t, change_state, TNC_Result,
490 private_imv_agent_t *this, TNC_ConnectionID connection_id,
74012695
AS
491 TNC_ConnectionState new_state,
492 imv_state_t **state_p)
510f37ab
AS
493{
494 imv_state_t *state;
495
496 switch (new_state)
497 {
498 case TNC_CONNECTION_STATE_HANDSHAKE:
499 case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
500 case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
501 case TNC_CONNECTION_STATE_ACCESS_NONE:
502 state = find_connection(this, connection_id);
503 if (!state)
504 {
505 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
506 this->id, this->name, connection_id);
507 return TNC_RESULT_FATAL;
508 }
509 state->change_state(state, new_state);
510 DBG2(DBG_IMV, "IMV %u \"%s\" changed state of Connection ID %u to '%N'",
511 this->id, this->name, connection_id,
512 TNC_Connection_State_names, new_state);
74012695
AS
513 if (state_p)
514 {
515 *state_p = state;
516 }
510f37ab
AS
517 break;
518 case TNC_CONNECTION_STATE_CREATE:
519 DBG1(DBG_IMV, "state '%N' should be handled by create_state()",
520 TNC_Connection_State_names, new_state);
521 return TNC_RESULT_FATAL;
522 case TNC_CONNECTION_STATE_DELETE:
523 DBG1(DBG_IMV, "state '%N' should be handled by delete_state()",
524 TNC_Connection_State_names, new_state);
525 return TNC_RESULT_FATAL;
526 default:
527 DBG1(DBG_IMV, "IMV %u \"%s\" was notified of unknown state %u "
528 "for Connection ID %u",
529 this->id, this->name, new_state, connection_id);
a05f3b20 530 return TNC_RESULT_INVALID_PARAMETER;
510f37ab
AS
531 }
532 return TNC_RESULT_SUCCESS;
533}
534
535METHOD(imv_agent_t, get_state, bool,
536 private_imv_agent_t *this, TNC_ConnectionID connection_id,
537 imv_state_t **state)
538{
539 *state = find_connection(this, connection_id);
540 if (!*state)
541 {
542 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
543 this->id, this->name, connection_id);
544 return FALSE;
545 }
546 return TRUE;
547}
548
6a61b795
AS
549METHOD(imv_agent_t, get_name, const char*,
550 private_imv_agent_t *this)
551{
552 return this->name;
553}
554
555METHOD(imv_agent_t, get_id, TNC_IMVID,
556 private_imv_agent_t *this)
557{
558 return this->id;
559}
560
cbf2ba54
AS
561METHOD(imv_agent_t, reserve_additional_ids, TNC_Result,
562 private_imv_agent_t *this, int count)
ac3331e1 563{
cbf2ba54
AS
564 TNC_Result result;
565 TNC_UInt32 id;
566 void *pointer;
567
ac3331e1
AS
568 if (!this->reserve_additional_id)
569 {
cbf2ba54
AS
570 DBG1(DBG_IMV, "IMV %u \"%s\" did not detect the capability to reserve "
571 "additional IMV IDs from the TNCS", this->id, this->name);
ac3331e1
AS
572 return TNC_RESULT_ILLEGAL_OPERATION;
573 }
cbf2ba54
AS
574 while (count > 0)
575 {
576 result = this->reserve_additional_id(this->id, &id);
577 if (result != TNC_RESULT_SUCCESS)
578 {
579 DBG1(DBG_IMV, "IMV %u \"%s\" failed to reserve %d additional IMV IDs",
580 this->id, this->name, count);
581 return result;
582 }
583 count--;
584
585 /* store the scalar value in the pointer */
6b98c002 586 pointer = (void*)(uintptr_t)id;
cbf2ba54
AS
587 this->additional_ids->insert_last(this->additional_ids, pointer);
588 DBG2(DBG_IMV, "IMV %u \"%s\" reserved additional ID %u",
589 this->id, this->name, id);
590 }
591 return TNC_RESULT_SUCCESS;
592}
593
594METHOD(imv_agent_t, count_additional_ids, int,
595 private_imv_agent_t *this)
596{
597 return this->additional_ids->get_count(this->additional_ids);
598}
599
600METHOD(imv_agent_t, create_id_enumerator, enumerator_t*,
601 private_imv_agent_t *this)
602{
603 return this->additional_ids->create_enumerator(this->additional_ids);
ac3331e1
AS
604}
605
db15c6da
AS
606typedef struct {
607 /**
608 * implements enumerator_t
609 */
610 enumerator_t public;
611
612 /**
613 * language length
614 */
615 TNC_UInt32 lang_len;
616
617 /**
618 * language buffer
619 */
620 char lang_buf[BUF_LEN];
621
622 /**
623 * position pointer into language buffer
624 */
625 char *lang_pos;
626
627} language_enumerator_t;
628
629/**
630 * Implementation of language_enumerator.destroy.
631 */
632static void language_enumerator_destroy(language_enumerator_t *this)
633{
634 free(this);
635}
636
637/**
638 * Implementation of language_enumerator.enumerate
639 */
640static bool language_enumerator_enumerate(language_enumerator_t *this, ...)
641{
642 char *pos, *cur_lang, **lang;
643 TNC_UInt32 len;
644 va_list args;
645
646 if (!this->lang_len)
647 {
648 return FALSE;
649 }
650 cur_lang = this->lang_pos;
651 pos = strchr(this->lang_pos, ',');
652 if (pos)
653 {
654 len = pos - this->lang_pos;
655 this->lang_pos += len + 1,
656 this->lang_len -= len + 1;
657 }
658 else
659 {
db15c6da 660 len = this->lang_len;
af83700f 661 pos = this->lang_pos + len;
db15c6da
AS
662 this->lang_pos = NULL;
663 this->lang_len = 0;
664 }
665
666 /* remove preceding whitespace */
667 while (*cur_lang == ' ' && len--)
668 {
669 cur_lang++;
670 }
671
672 /* remove trailing whitespace */
673 while (len && *(--pos) == ' ')
674 {
675 len--;
676 }
677 cur_lang[len] = '\0';
db15c6da
AS
678
679 va_start(args, this);
680 lang = va_arg(args, char**);
681 *lang = cur_lang;
682 va_end(args);
683
684 return TRUE;
685}
686
687METHOD(imv_agent_t, create_language_enumerator, enumerator_t*,
688 private_imv_agent_t *this, imv_state_t *state)
689{
690 language_enumerator_t *e;
691
692 /* Create a language enumerator instance */
693 e = malloc_thing(language_enumerator_t);
af83700f
AS
694 e->public.enumerate = (void*)language_enumerator_enumerate;
695 e->public.destroy = (void*)language_enumerator_destroy;
db15c6da 696
af83700f 697 if (!this->get_attribute ||
db15c6da
AS
698 !this->get_attribute(this->id, state->get_connection_id(state),
699 TNC_ATTRIBUTEID_PREFERRED_LANGUAGE, BUF_LEN,
700 e->lang_buf, &e->lang_len) == TNC_RESULT_SUCCESS ||
701 e->lang_len >= BUF_LEN)
702 {
af83700f 703 e->lang_len = 0;
db15c6da 704 }
db15c6da
AS
705 e->lang_buf[e->lang_len] = '\0';
706 e->lang_pos = e->lang_buf;
707
708 return (enumerator_t*)e;
709}
710
711METHOD(imv_agent_t, provide_recommendation, TNC_Result,
712 private_imv_agent_t *this, imv_state_t *state)
713{
714 TNC_IMV_Action_Recommendation rec;
715 TNC_IMV_Evaluation_Result eval;
716 TNC_ConnectionID connection_id;
ee6aeca8
AS
717 chunk_t reason_string;
718 char *reason_lang;
db15c6da
AS
719 enumerator_t *e;
720
721 state->get_recommendation(state, &rec, &eval);
722 connection_id = state->get_connection_id(state);
723
724 /* send a reason string if action recommendation is not allow */
725 if (rec != TNC_IMV_ACTION_RECOMMENDATION_ALLOW)
726 {
727 /* find a reason string for the preferred language and set it */
728 if (this->set_attribute)
729 {
730 e = create_language_enumerator(this, state);
731 if (state->get_reason_string(state, e, &reason_string, &reason_lang))
732 {
733 this->set_attribute(this->id, connection_id,
734 TNC_ATTRIBUTEID_REASON_STRING,
ee6aeca8 735 reason_string.len, reason_string.ptr);
db15c6da
AS
736 this->set_attribute(this->id, connection_id,
737 TNC_ATTRIBUTEID_REASON_LANGUAGE,
738 strlen(reason_lang), reason_lang);
739 }
740 e->destroy(e);
741 }
742 }
743 return this->provide_recommendation(this->id, connection_id, rec, eval);
744}
745
38b5f527
AS
746METHOD(imv_agent_t, add_non_fatal_attr_type, void,
747 private_imv_agent_t *this, pen_type_t type)
748{
749 pen_type_t *type_p;
750
751 type_p = malloc_thing(pen_type_t);
752 *type_p = type;
753 this->non_fatal_attr_types->insert_last(this->non_fatal_attr_types, type_p);
754}
755
756METHOD(imv_agent_t, get_non_fatal_attr_types, linked_list_t*,
757 private_imv_agent_t *this)
758{
759 return this->non_fatal_attr_types;
760}
761
510f37ab
AS
762METHOD(imv_agent_t, destroy, void,
763 private_imv_agent_t *this)
764{
54b622c3 765 DBG1(DBG_IMV, "IMV %u \"%s\" terminated", this->id, this->name);
cbf2ba54 766 this->additional_ids->destroy(this->additional_ids);
38b5f527
AS
767 this->non_fatal_attr_types->destroy_function(this->non_fatal_attr_types,
768 free);
510f37ab
AS
769 this->connections->destroy_offset(this->connections,
770 offsetof(imv_state_t, destroy));
771 this->connection_lock->destroy(this->connection_lock);
772 free(this);
d4c8fe3c
AS
773
774 /* decrease the reference count or terminate */
775 libimcv_deinit();
510f37ab
AS
776}
777
778/**
779 * Described in header.
780 */
781imv_agent_t *imv_agent_create(const char *name,
4894bfa2 782 pen_type_t *supported_types, uint32_t type_count,
510f37ab
AS
783 TNC_IMVID id, TNC_Version *actual_version)
784{
785 private_imv_agent_t *this;
786
d4c8fe3c 787 /* initialize or increase the reference count */
a6266485 788 if (!libimcv_init(TRUE))
d4c8fe3c
AS
789 {
790 return NULL;
791 }
792
510f37ab
AS
793 INIT(this,
794 .public = {
795 .bind_functions = _bind_functions,
796 .create_state = _create_state,
797 .delete_state = _delete_state,
798 .change_state = _change_state,
799 .get_state = _get_state,
6a61b795
AS
800 .get_name = _get_name,
801 .get_id = _get_id,
cbf2ba54
AS
802 .reserve_additional_ids = _reserve_additional_ids,
803 .count_additional_ids = _count_additional_ids,
804 .create_id_enumerator = _create_id_enumerator,
db15c6da
AS
805 .create_language_enumerator = _create_language_enumerator,
806 .provide_recommendation = _provide_recommendation,
38b5f527
AS
807 .add_non_fatal_attr_type = _add_non_fatal_attr_type,
808 .get_non_fatal_attr_types = _get_non_fatal_attr_types,
510f37ab
AS
809 .destroy = _destroy,
810 },
811 .name = name,
5f085d7e
AS
812 .supported_types = supported_types,
813 .type_count = type_count,
510f37ab 814 .id = id,
cbf2ba54 815 .additional_ids = linked_list_create(),
38b5f527 816 .non_fatal_attr_types = linked_list_create(),
510f37ab
AS
817 .connections = linked_list_create(),
818 .connection_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
819 );
d4c8fe3c 820
510f37ab
AS
821 *actual_version = TNC_IFIMV_VERSION_1;
822 DBG1(DBG_IMV, "IMV %u \"%s\" initialized", this->id, this->name);
823
824 return &this->public;
825}