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