Implemented IF-M segmentation
[strongswan.git] / src / libimcv / imc / imc_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 "imc_agent.h"
18
19 #include <tncif_names.h>
20
21 #include <utils/debug.h>
22 #include <threading/rwlock.h>
23
24 typedef struct private_imc_agent_t private_imc_agent_t;
25
26 /**
27 * Private data of an imc_agent_t object.
28 */
29 struct private_imc_agent_t {
30
31 /**
32 * Public members of imc_agent_t
33 */
34 imc_agent_t public;
35
36 /**
37 * name of IMC
38 */
39 const char *name;
40
41 /**
42 * message types registered by IMC
43 */
44 pen_type_t *supported_types;
45
46 /**
47 * number of message types registered by IMC
48 */
49 u_int32_t type_count;
50
51 /**
52 * ID of IMC as assigned by TNCC
53 */
54 TNC_IMCID id;
55
56 /**
57 * List of additional IMC IDs assigned by TNCC
58 */
59 linked_list_t *additional_ids;
60
61 /**
62 * list of non-fatal unsupported PA-TNC attribute types
63 */
64 linked_list_t *non_fatal_attr_types;
65
66 /**
67 * list of TNCC connection entries
68 */
69 linked_list_t *connections;
70
71 /**
72 * rwlock to lock TNCC connection entries
73 */
74 rwlock_t *connection_lock;
75
76 /**
77 * Inform a TNCC about the set of message types the IMC is able to receive
78 *
79 * @param imc_id IMC ID assigned by TNCC
80 * @param supported_types list of supported message types
81 * @param type_count number of list elements
82 * @return TNC result code
83 */
84 TNC_Result (*report_message_types)(TNC_IMCID imc_id,
85 TNC_MessageTypeList supported_types,
86 TNC_UInt32 type_count);
87
88 /**
89 * Inform a TNCC about the set of message types the IMC is able to receive
90 *
91 * @param imc_id IMC ID assigned by TNCC
92 * @param supported_vids list of supported message vendor IDs
93 * @param supported_subtypes list of supported message subtypes
94 * @param type_count number of list elements
95 * @return TNC result code
96 */
97 TNC_Result (*report_message_types_long)(TNC_IMCID imc_id,
98 TNC_VendorIDList supported_vids,
99 TNC_MessageSubtypeList supported_subtypes,
100 TNC_UInt32 type_count);
101
102 /**
103 * Get the value of an attribute associated with a connection
104 * or with the TNCC as a whole.
105 *
106 * @param imc_id IMC ID assigned by TNCC
107 * @param connection_id network connection ID assigned by TNCC
108 * @param attribute_id attribute ID
109 * @param buffer_len length of buffer in bytes
110 * @param buffer buffer
111 * @param out_value_len size in bytes of attribute stored in buffer
112 * @return TNC result code
113 */
114 TNC_Result (*get_attribute)(TNC_IMCID imc_id,
115 TNC_ConnectionID connection_id,
116 TNC_AttributeID attribute_id,
117 TNC_UInt32 buffer_len,
118 TNC_BufferReference buffer,
119 TNC_UInt32 *out_value_len);
120
121 /**
122 * Set the value of an attribute associated with a connection
123 * or with the TNCC as a whole.
124 *
125 * @param imc_id IMV ID assigned by TNCC
126 * @param connection_id network connection ID assigned by TNCC
127 * @param attribute_id attribute ID
128 * @param buffer_len length of buffer in bytes
129 * @param buffer buffer
130 * @return TNC result code
131 */
132 TNC_Result (*set_attribute)(TNC_IMCID imc_id,
133 TNC_ConnectionID connection_id,
134 TNC_AttributeID attribute_id,
135 TNC_UInt32 buffer_len,
136 TNC_BufferReference buffer);
137
138 /**
139 * Reserve an additional IMC ID
140 *
141 * @param imc_id primary IMC ID assigned by TNCC
142 * @param out_imc_id additional IMC ID assigned by TNCC
143 * @return TNC result code
144 */
145 TNC_Result (*reserve_additional_id)(TNC_IMCID imc_id,
146 TNC_UInt32 *out_imc_id);
147
148 };
149
150 METHOD(imc_agent_t, bind_functions, TNC_Result,
151 private_imc_agent_t *this, TNC_TNCC_BindFunctionPointer bind_function)
152 {
153 if (!bind_function)
154 {
155 DBG1(DBG_IMC, "TNC client failed to provide bind function");
156 return TNC_RESULT_INVALID_PARAMETER;
157 }
158 if (bind_function(this->id, "TNC_TNCC_ReportMessageTypes",
159 (void**)&this->report_message_types) != TNC_RESULT_SUCCESS)
160 {
161 this->report_message_types = NULL;
162 }
163 if (bind_function(this->id, "TNC_TNCC_ReportMessageTypesLong",
164 (void**)&this->report_message_types_long) != TNC_RESULT_SUCCESS)
165 {
166 this->report_message_types_long = NULL;
167 }
168 if (bind_function(this->id, "TNC_TNCC_RequestHandshakeRetry",
169 (void**)&this->public.request_handshake_retry) != TNC_RESULT_SUCCESS)
170 {
171 this->public.request_handshake_retry = NULL;
172 }
173 if (bind_function(this->id, "TNC_TNCC_SendMessage",
174 (void**)&this->public.send_message) != TNC_RESULT_SUCCESS)
175 {
176 this->public.send_message = NULL;
177 }
178 if (bind_function(this->id, "TNC_TNCC_SendMessageLong",
179 (void**)&this->public.send_message_long) != TNC_RESULT_SUCCESS)
180 {
181 this->public.send_message_long = NULL;
182 }
183 if (bind_function(this->id, "TNC_TNCC_GetAttribute",
184 (void**)&this->get_attribute) != TNC_RESULT_SUCCESS)
185 {
186 this->get_attribute = NULL;
187 }
188 if (bind_function(this->id, "TNC_TNCC_SetAttribute",
189 (void**)&this->set_attribute) != TNC_RESULT_SUCCESS)
190 {
191 this->set_attribute = NULL;
192 }
193 if (bind_function(this->id, "TNC_TNCC_ReserveAdditionalIMCID",
194 (void**)&this->reserve_additional_id) != TNC_RESULT_SUCCESS)
195 {
196 this->reserve_additional_id = NULL;
197 }
198
199 if (this->report_message_types_long)
200 {
201 TNC_VendorIDList vendor_id_list;
202 TNC_MessageSubtypeList subtype_list;
203 int i;
204
205 vendor_id_list = malloc(this->type_count * sizeof(TNC_UInt32));
206 subtype_list = malloc(this->type_count * sizeof(TNC_UInt32));
207
208 for (i = 0; i < this->type_count; i++)
209 {
210 vendor_id_list[i] = this->supported_types[i].vendor_id;
211 subtype_list[i] = this->supported_types[i].type;
212 }
213 this->report_message_types_long(this->id, vendor_id_list, subtype_list,
214 this->type_count);
215 free(vendor_id_list);
216 free(subtype_list);
217 }
218 else if (this->report_message_types)
219 {
220 TNC_MessageTypeList type_list;
221 int i;
222
223 type_list = malloc(this->type_count * sizeof(TNC_UInt32));
224
225 for (i = 0; i < this->type_count; i++)
226 {
227 type_list[i] = (this->supported_types[i].vendor_id << 8) |
228 (this->supported_types[i].type & 0xff);
229 }
230 this->report_message_types(this->id, type_list, this->type_count);
231 free(type_list);
232 }
233 return TNC_RESULT_SUCCESS;
234 }
235
236 /**
237 * finds a connection state based on its Connection ID
238 */
239 static imc_state_t* find_connection(private_imc_agent_t *this,
240 TNC_ConnectionID id)
241 {
242 enumerator_t *enumerator;
243 imc_state_t *state, *found = NULL;
244
245 this->connection_lock->read_lock(this->connection_lock);
246 enumerator = this->connections->create_enumerator(this->connections);
247 while (enumerator->enumerate(enumerator, &state))
248 {
249 if (id == state->get_connection_id(state))
250 {
251 found = state;
252 break;
253 }
254 }
255 enumerator->destroy(enumerator);
256 this->connection_lock->unlock(this->connection_lock);
257
258 return found;
259 }
260
261 /**
262 * delete a connection state with a given Connection ID
263 */
264 static bool delete_connection(private_imc_agent_t *this, TNC_ConnectionID id)
265 {
266 enumerator_t *enumerator;
267 imc_state_t *state;
268 bool found = FALSE;
269
270 this->connection_lock->write_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 = TRUE;
277 state->destroy(state);
278 this->connections->remove_at(this->connections, enumerator);
279 break;
280 }
281 }
282 enumerator->destroy(enumerator);
283 this->connection_lock->unlock(this->connection_lock);
284
285 return found;
286 }
287
288 /**
289 * Read a boolean attribute
290 */
291 static bool get_bool_attribute(private_imc_agent_t *this, TNC_ConnectionID id,
292 TNC_AttributeID attribute_id)
293 {
294 TNC_UInt32 len;
295 char buf[4];
296
297 return this->get_attribute &&
298 this->get_attribute(this->id, id, attribute_id, 4, buf, &len) ==
299 TNC_RESULT_SUCCESS && len == 1 && *buf == 0x01;
300 }
301
302 /**
303 * Read a string attribute
304 */
305 static char* get_str_attribute(private_imc_agent_t *this, TNC_ConnectionID id,
306 TNC_AttributeID attribute_id)
307 {
308 TNC_UInt32 len;
309 char buf[BUF_LEN];
310
311 if (this->get_attribute &&
312 this->get_attribute(this->id, id, attribute_id, BUF_LEN, buf, &len) ==
313 TNC_RESULT_SUCCESS && len <= BUF_LEN)
314 {
315 return strdup(buf);
316 }
317 return NULL;
318 }
319
320 /**
321 * Read an UInt32 attribute
322 */
323 static u_int32_t get_uint_attribute(private_imc_agent_t *this, TNC_ConnectionID id,
324 TNC_AttributeID attribute_id)
325 {
326 TNC_UInt32 len;
327 char buf[4];
328
329 if (this->get_attribute &&
330 this->get_attribute(this->id, id, attribute_id, 4, buf, &len) ==
331 TNC_RESULT_SUCCESS && len == 4)
332 {
333 return untoh32(buf);
334 }
335 return 0;
336 }
337
338 METHOD(imc_agent_t, create_state, TNC_Result,
339 private_imc_agent_t *this, imc_state_t *state)
340 {
341 TNC_ConnectionID conn_id;
342 char *tnccs_p = NULL, *tnccs_v = NULL, *t_p = NULL, *t_v = NULL;
343 bool has_long = FALSE, has_excl = FALSE, has_soh = FALSE;
344 u_int32_t max_msg_len;
345
346 conn_id = state->get_connection_id(state);
347 if (find_connection(this, conn_id))
348 {
349 DBG1(DBG_IMC, "IMC %u \"%s\" already created a state for Connection ID %u",
350 this->id, this->name, conn_id);
351 state->destroy(state);
352 return TNC_RESULT_OTHER;
353 }
354
355 /* Get and display attributes from TNCC via IF-IMC */
356 has_long = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_LONG_TYPES);
357 has_excl = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_EXCLUSIVE);
358 has_soh = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_SOH);
359 tnccs_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL);
360 tnccs_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_VERSION);
361 t_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_PROTOCOL);
362 t_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_VERSION);
363 max_msg_len = get_uint_attribute(this, conn_id, TNC_ATTRIBUTEID_MAX_MESSAGE_SIZE);
364
365 state->set_flags(state, has_long, has_excl);
366 state->set_max_msg_len(state, max_msg_len);
367
368 DBG2(DBG_IMC, "IMC %u \"%s\" created a state for %s %s Connection ID %u: "
369 "%slong %sexcl %ssoh", this->id, this->name,
370 tnccs_p ? tnccs_p:"?", tnccs_v ? tnccs_v:"?", conn_id,
371 has_long ? "+":"-", has_excl ? "+":"-", has_soh ? "+":"-");
372 DBG2(DBG_IMC, " over %s %s with maximum PA-TNC message size of %u bytes",
373 t_p ? t_p:"?", t_v ? t_v :"?", max_msg_len);
374
375 free(tnccs_p);
376 free(tnccs_v);
377 free(t_p);
378 free(t_v);
379
380 this->connection_lock->write_lock(this->connection_lock);
381 this->connections->insert_last(this->connections, state);
382 this->connection_lock->unlock(this->connection_lock);
383 return TNC_RESULT_SUCCESS;
384 }
385
386 METHOD(imc_agent_t, delete_state, TNC_Result,
387 private_imc_agent_t *this, TNC_ConnectionID connection_id)
388 {
389 if (!delete_connection(this, connection_id))
390 {
391 DBG1(DBG_IMC, "IMC %u \"%s\" has no state for Connection ID %u",
392 this->id, this->name, connection_id);
393 return TNC_RESULT_FATAL;
394 }
395 DBG2(DBG_IMC, "IMC %u \"%s\" deleted the state of Connection ID %u",
396 this->id, this->name, connection_id);
397 return TNC_RESULT_SUCCESS;
398 }
399
400 METHOD(imc_agent_t, change_state, TNC_Result,
401 private_imc_agent_t *this, TNC_ConnectionID connection_id,
402 TNC_ConnectionState new_state,
403 imc_state_t **state_p)
404 {
405 imc_state_t *state;
406
407 switch (new_state)
408 {
409 case TNC_CONNECTION_STATE_HANDSHAKE:
410 case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
411 case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
412 case TNC_CONNECTION_STATE_ACCESS_NONE:
413 state = find_connection(this, connection_id);
414
415 if (!state)
416 {
417 DBG1(DBG_IMC, "IMC %u \"%s\" has no state for Connection ID %u",
418 this->id, this->name, connection_id);
419 return TNC_RESULT_FATAL;
420 }
421 state->change_state(state, new_state);
422 DBG2(DBG_IMC, "IMC %u \"%s\" changed state of Connection ID %u to '%N'",
423 this->id, this->name, connection_id,
424 TNC_Connection_State_names, new_state);
425 if (state_p)
426 {
427 *state_p = state;
428 }
429 break;
430 case TNC_CONNECTION_STATE_CREATE:
431 DBG1(DBG_IMC, "state '%N' should be handled by create_state()",
432 TNC_Connection_State_names, new_state);
433 return TNC_RESULT_FATAL;
434 case TNC_CONNECTION_STATE_DELETE:
435 DBG1(DBG_IMC, "state '%N' should be handled by delete_state()",
436 TNC_Connection_State_names, new_state);
437 return TNC_RESULT_FATAL;
438 default:
439 DBG1(DBG_IMC, "IMC %u \"%s\" was notified of unknown state %u "
440 "for Connection ID %u",
441 this->id, this->name, new_state, connection_id);
442 return TNC_RESULT_INVALID_PARAMETER;
443 }
444 return TNC_RESULT_SUCCESS;
445 }
446
447 METHOD(imc_agent_t, get_state, bool,
448 private_imc_agent_t *this, TNC_ConnectionID connection_id,
449 imc_state_t **state)
450 {
451 *state = find_connection(this, connection_id);
452 if (!*state)
453 {
454 DBG1(DBG_IMC, "IMC %u \"%s\" has no state for Connection ID %u",
455 this->id, this->name, connection_id);
456 return FALSE;
457 }
458 return TRUE;
459 }
460
461 METHOD(imc_agent_t, get_name, const char*,
462 private_imc_agent_t *this)
463 {
464 return this->name;
465 }
466
467 METHOD(imc_agent_t, get_id, TNC_IMCID,
468 private_imc_agent_t *this)
469 {
470 return this->id;
471 }
472
473 METHOD(imc_agent_t, reserve_additional_ids, TNC_Result,
474 private_imc_agent_t *this, int count)
475 {
476 TNC_Result result;
477 TNC_UInt32 id;
478 void *pointer;
479
480 if (!this->reserve_additional_id)
481 {
482 DBG1(DBG_IMC, "IMC %u \"%s\" did not detect the capability to reserve "
483 "additional IMC IDs from the TNCC", this->id, this->name);
484 return TNC_RESULT_ILLEGAL_OPERATION;
485 }
486 while (count > 0)
487 {
488 result = this->reserve_additional_id(this->id, &id);
489 if (result != TNC_RESULT_SUCCESS)
490 {
491 DBG1(DBG_IMC, "IMC %u \"%s\" failed to reserve %d additional IMC IDs",
492 this->id, this->name, count);
493 return result;
494 }
495 count--;
496
497 /* store the scalar value in the pointer */
498 pointer = (void*)(uintptr_t)id;
499 this->additional_ids->insert_last(this->additional_ids, pointer);
500 DBG2(DBG_IMC, "IMC %u \"%s\" reserved additional ID %u",
501 this->id, this->name, id);
502 }
503 return TNC_RESULT_SUCCESS;
504 }
505
506 METHOD(imc_agent_t, count_additional_ids, int,
507 private_imc_agent_t *this)
508 {
509 return this->additional_ids->get_count(this->additional_ids);
510 }
511
512 METHOD(imc_agent_t, create_id_enumerator, enumerator_t*,
513 private_imc_agent_t *this)
514 {
515 return this->additional_ids->create_enumerator(this->additional_ids);
516 }
517
518 METHOD(imc_agent_t, add_non_fatal_attr_type, void,
519 private_imc_agent_t *this, pen_type_t type)
520 {
521 pen_type_t *type_p;
522
523 type_p = malloc_thing(pen_type_t);
524 *type_p = type;
525 this->non_fatal_attr_types->insert_last(this->non_fatal_attr_types, type_p);
526 }
527
528 METHOD(imc_agent_t, get_non_fatal_attr_types, linked_list_t*,
529 private_imc_agent_t *this)
530 {
531 return this->non_fatal_attr_types;
532 }
533
534 METHOD(imc_agent_t, destroy, void,
535 private_imc_agent_t *this)
536 {
537 DBG1(DBG_IMC, "IMC %u \"%s\" terminated", this->id, this->name);
538 this->additional_ids->destroy(this->additional_ids);
539 this->non_fatal_attr_types->destroy_function(this->non_fatal_attr_types,
540 free);
541 this->connections->destroy_function(this->connections, free);
542 this->connection_lock->destroy(this->connection_lock);
543 free(this);
544
545 /* decrease the reference count or terminate */
546 libimcv_deinit();
547 }
548
549 /**
550 * Described in header.
551 */
552 imc_agent_t *imc_agent_create(const char *name,
553 pen_type_t *supported_types, u_int32_t type_count,
554 TNC_IMCID id, TNC_Version *actual_version)
555 {
556 private_imc_agent_t *this;
557
558 /* initialize or increase the reference count */
559 if (!libimcv_init(FALSE))
560 {
561 return NULL;
562 }
563
564 INIT(this,
565 .public = {
566 .bind_functions = _bind_functions,
567 .create_state = _create_state,
568 .delete_state = _delete_state,
569 .change_state = _change_state,
570 .get_state = _get_state,
571 .get_name = _get_name,
572 .get_id = _get_id,
573 .reserve_additional_ids = _reserve_additional_ids,
574 .count_additional_ids = _count_additional_ids,
575 .create_id_enumerator = _create_id_enumerator,
576 .add_non_fatal_attr_type = _add_non_fatal_attr_type,
577 .get_non_fatal_attr_types = _get_non_fatal_attr_types,
578 .destroy = _destroy,
579 },
580 .name = name,
581 .supported_types = supported_types,
582 .type_count = type_count,
583 .id = id,
584 .additional_ids = linked_list_create(),
585 .non_fatal_attr_types = linked_list_create(),
586 .connections = linked_list_create(),
587 .connection_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
588 );
589
590 *actual_version = TNC_IFIMC_VERSION_1;
591 DBG1(DBG_IMC, "IMC %u \"%s\" initialized", this->id, this->name);
592
593 return &this->public;
594 }