b950e3119b9f5c8679b3f32c44fcd0952a3f6c63
[strongswan.git] / src / libcharon / plugins / tnc_imv / tnc_imv_manager.c
1 /*
2 * Copyright (C) 2006 Mike McCauley
3 * Copyright (C) 2010-2011 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "tnc_imv_manager.h"
18 #include "tnc_imv.h"
19 #include "tnc_imv_recommendations.h"
20
21 #include <tncifimv.h>
22 #include <tncif_names.h>
23
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/mman.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <fcntl.h>
30
31 #include <daemon.h>
32 #include <utils/debug.h>
33 #include <threading/rwlock.h>
34 #include <threading/mutex.h>
35 #include <collections/linked_list.h>
36
37 typedef struct private_tnc_imv_manager_t private_tnc_imv_manager_t;
38
39 /**
40 * Private data of an imv_manager_t object.
41 */
42 struct private_tnc_imv_manager_t {
43
44 /**
45 * Public members of imv_manager_t.
46 */
47 imv_manager_t public;
48
49 /**
50 * Linked list of IMVs
51 */
52 linked_list_t *imvs;
53
54 /**
55 * Lock for IMV list
56 */
57 rwlock_t *lock;
58
59 /**
60 * Next IMV ID to be assigned
61 */
62 TNC_IMVID next_imv_id;
63
64 /**
65 * Mutex to access next IMV ID
66 */
67 mutex_t *id_mutex;
68
69 /**
70 * Policy defining how to derive final recommendation from individual ones
71 */
72 recommendation_policy_t policy;
73 };
74
75 METHOD(imv_manager_t, add, bool,
76 private_tnc_imv_manager_t *this, imv_t *imv)
77 {
78 TNC_Version version;
79 TNC_IMVID imv_id;
80
81 this->id_mutex->lock(this->id_mutex);
82 imv_id = this->next_imv_id++;
83 this->id_mutex->unlock(this->id_mutex);
84
85 imv->set_id(imv, imv_id);
86 if (imv->initialize(imv_id, TNC_IFIMV_VERSION_1,
87 TNC_IFIMV_VERSION_1, &version) != TNC_RESULT_SUCCESS)
88 {
89 DBG1(DBG_TNC, "IMV \"%s\" failed to initialize", imv->get_name(imv));
90 return FALSE;
91 }
92 this->lock->write_lock(this->lock);
93 this->imvs->insert_last(this->imvs, imv);
94 this->lock->unlock(this->lock);
95
96 if (imv->provide_bind_function(imv->get_id(imv),
97 TNC_TNCS_BindFunction) != TNC_RESULT_SUCCESS)
98 {
99 if (imv->terminate)
100 {
101 imv->terminate(imv->get_id(imv));
102 }
103 DBG1(DBG_TNC, "IMV \"%s\" failed to obtain bind function",
104 imv->get_name(imv));
105 this->lock->write_lock(this->lock);
106 this->imvs->remove_last(this->imvs, (void**)&imv);
107 this->lock->unlock(this->lock);
108 return FALSE;
109 }
110 return TRUE;
111 }
112
113 METHOD(imv_manager_t, remove_, imv_t*,
114 private_tnc_imv_manager_t *this, TNC_IMVID id)
115 {
116 enumerator_t *enumerator;
117 imv_t *imv, *removed_imv = NULL;
118
119 this->lock->write_lock(this->lock);
120 enumerator = this->imvs->create_enumerator(this->imvs);
121 while (enumerator->enumerate(enumerator, &imv))
122 {
123 if (id == imv->get_id(imv))
124 {
125 this->imvs->remove_at(this->imvs, enumerator);
126 removed_imv = imv;
127 break;
128 }
129 }
130 enumerator->destroy(enumerator);
131 this->lock->unlock(this->lock);
132
133 return removed_imv;
134 }
135
136 METHOD(imv_manager_t, load, bool,
137 private_tnc_imv_manager_t *this, char *name, char *path)
138 {
139 imv_t *imv;
140
141 imv = tnc_imv_create(name, path);
142 if (!imv)
143 {
144 return FALSE;
145 }
146 if (!add(this, imv))
147 {
148 imv->destroy(imv);
149 return FALSE;
150 }
151 DBG1(DBG_TNC, "IMV %u \"%s\" loaded from '%s'", imv->get_id(imv), name, path);
152 return TRUE;
153 }
154
155 METHOD(imv_manager_t, load_from_functions, bool,
156 private_tnc_imv_manager_t *this, char *name,
157 TNC_IMV_InitializePointer initialize,
158 TNC_IMV_NotifyConnectionChangePointer notify_connection_change,
159 TNC_IMV_ReceiveMessagePointer receive_message,
160 TNC_IMV_ReceiveMessageLongPointer receive_message_long,
161 TNC_IMV_SolicitRecommendationPointer solicit_recommendation,
162 TNC_IMV_BatchEndingPointer batch_ending,
163 TNC_IMV_TerminatePointer terminate,
164 TNC_IMV_ProvideBindFunctionPointer provide_bind_function)
165 {
166 imv_t *imv;
167
168 imv = tnc_imv_create_from_functions(name,
169 initialize,notify_connection_change,
170 receive_message, receive_message_long,
171 solicit_recommendation, batch_ending,
172 terminate, provide_bind_function);
173 if (!imv)
174 {
175 return FALSE;
176 }
177 if (!add(this, imv))
178 {
179 imv->destroy(imv);
180 return FALSE;
181 }
182 DBG1(DBG_TNC, "IMV %u \"%s\" loaded", imv->get_id(imv), name);
183 return TRUE;
184 }
185
186 METHOD(imv_manager_t, is_registered, bool,
187 private_tnc_imv_manager_t *this, TNC_IMVID id)
188 {
189 enumerator_t *enumerator;
190 imv_t *imv;
191 bool found = FALSE;
192
193 this->lock->read_lock(this->lock);
194 enumerator = this->imvs->create_enumerator(this->imvs);
195 while (enumerator->enumerate(enumerator, &imv))
196 {
197 if (imv->has_id(imv, id))
198 {
199 found = TRUE;
200 break;
201 }
202 }
203 enumerator->destroy(enumerator);
204 this->lock->unlock(this->lock);
205
206 return found;
207 }
208
209 METHOD(imv_manager_t, reserve_id, bool,
210 private_tnc_imv_manager_t *this, TNC_IMVID id, TNC_UInt32 *new_id)
211 {
212 enumerator_t *enumerator;
213 imv_t *imv;
214 bool found = FALSE;
215
216 this->lock->read_lock(this->lock);
217 enumerator = this->imvs->create_enumerator(this->imvs);
218 while (enumerator->enumerate(enumerator, &imv))
219 {
220 if (id == imv->get_id(imv))
221 {
222 found = TRUE;
223 this->id_mutex->lock(this->id_mutex);
224 *new_id = this->next_imv_id++;
225 this->id_mutex->unlock(this->id_mutex);
226 imv->add_id(imv, *new_id);
227 DBG2(DBG_TNC, "additional ID %u reserved for IMV with primary ID %u",
228 *new_id, id);
229 break;
230 }
231 }
232 enumerator->destroy(enumerator);
233 this->lock->unlock(this->lock);
234
235 return found;
236 }
237
238 METHOD(imv_manager_t, get_recommendation_policy, recommendation_policy_t,
239 private_tnc_imv_manager_t *this)
240 {
241 return this->policy;
242 }
243
244 METHOD(imv_manager_t, create_recommendations, recommendations_t*,
245 private_tnc_imv_manager_t *this)
246 {
247 return tnc_imv_recommendations_create(this->imvs);
248 }
249
250 METHOD(imv_manager_t, enforce_recommendation, bool,
251 private_tnc_imv_manager_t *this, TNC_IMV_Action_Recommendation rec,
252 TNC_IMV_Evaluation_Result eval)
253 {
254 char *group;
255 identification_t *id;
256 ike_sa_t *ike_sa;
257 auth_cfg_t *auth;
258 bool no_access = FALSE;
259
260 DBG1(DBG_TNC, "final recommendation is '%N' and evaluation is '%N'",
261 TNC_IMV_Action_Recommendation_names, rec,
262 TNC_IMV_Evaluation_Result_names, eval);
263
264 switch (rec)
265 {
266 case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
267 group = "allow";
268 break;
269 case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
270 group = "isolate";
271 break;
272 case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
273 case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
274 default:
275 group = "no access";
276 no_access = TRUE;
277 break;
278 }
279
280 ike_sa = charon->bus->get_sa(charon->bus);
281 if (!ike_sa)
282 {
283 DBG1(DBG_TNC, "policy enforcement point did not find IKE_SA");
284 return FALSE;
285 }
286
287 id = ike_sa->get_other_id(ike_sa);
288 DBG0(DBG_TNC, "policy enforced on peer '%Y' is '%s'", id, group);
289
290 if (no_access)
291 {
292 return FALSE;
293 }
294 else
295 {
296 auth = ike_sa->get_auth_cfg(ike_sa, FALSE);
297 id = identification_create_from_string(group);
298 auth->add(auth, AUTH_RULE_GROUP, id);
299 DBG1(DBG_TNC, "policy enforcement point added group membership '%s'",
300 group);
301 }
302 return TRUE;
303 }
304
305
306 METHOD(imv_manager_t, notify_connection_change, void,
307 private_tnc_imv_manager_t *this, TNC_ConnectionID id,
308 TNC_ConnectionState state)
309 {
310 enumerator_t *enumerator;
311 imv_t *imv;
312
313 this->lock->read_lock(this->lock);
314 enumerator = this->imvs->create_enumerator(this->imvs);
315 while (enumerator->enumerate(enumerator, &imv))
316 {
317 if (imv->notify_connection_change)
318 {
319 imv->notify_connection_change(imv->get_id(imv), id, state);
320 }
321 }
322 enumerator->destroy(enumerator);
323 this->lock->unlock(this->lock);
324 }
325
326 METHOD(imv_manager_t, set_message_types, TNC_Result,
327 private_tnc_imv_manager_t *this, TNC_IMVID id,
328 TNC_MessageTypeList supported_types,
329 TNC_UInt32 type_count)
330 {
331 enumerator_t *enumerator;
332 imv_t *imv;
333 TNC_Result result = TNC_RESULT_FATAL;
334
335 this->lock->read_lock(this->lock);
336 enumerator = this->imvs->create_enumerator(this->imvs);
337 while (enumerator->enumerate(enumerator, &imv))
338 {
339 if (id == imv->get_id(imv))
340 {
341 imv->set_message_types(imv, supported_types, type_count);
342 result = TNC_RESULT_SUCCESS;
343 break;
344 }
345 }
346 enumerator->destroy(enumerator);
347 this->lock->unlock(this->lock);
348 return result;
349 }
350
351 METHOD(imv_manager_t, set_message_types_long, TNC_Result,
352 private_tnc_imv_manager_t *this, TNC_IMVID id,
353 TNC_VendorIDList supported_vids,
354 TNC_MessageSubtypeList supported_subtypes,
355 TNC_UInt32 type_count)
356 {
357 enumerator_t *enumerator;
358 imv_t *imv;
359 TNC_Result result = TNC_RESULT_FATAL;
360
361 this->lock->read_lock(this->lock);
362 enumerator = this->imvs->create_enumerator(this->imvs);
363 while (enumerator->enumerate(enumerator, &imv))
364 {
365 if (id == imv->get_id(imv))
366 {
367 imv->set_message_types_long(imv, supported_vids, supported_subtypes,
368 type_count);
369 result = TNC_RESULT_SUCCESS;
370 break;
371 }
372 }
373 enumerator->destroy(enumerator);
374 this->lock->unlock(this->lock);
375 return result;
376 }
377
378 METHOD(imv_manager_t, solicit_recommendation, void,
379 private_tnc_imv_manager_t *this, TNC_ConnectionID id)
380 {
381 enumerator_t *enumerator;
382 imv_t *imv;
383
384 this->lock->read_lock(this->lock);
385 enumerator = this->imvs->create_enumerator(this->imvs);
386 while (enumerator->enumerate(enumerator, &imv))
387 {
388 imv->solicit_recommendation(imv->get_id(imv), id);
389 }
390 enumerator->destroy(enumerator);
391 this->lock->unlock(this->lock);
392 }
393
394 METHOD(imv_manager_t, receive_message, void,
395 private_tnc_imv_manager_t *this, TNC_ConnectionID connection_id,
396 bool excl,
397 TNC_BufferReference msg,
398 TNC_UInt32 msg_len,
399 TNC_VendorID msg_vid,
400 TNC_MessageSubtype msg_subtype,
401 TNC_UInt32 src_imc_id,
402 TNC_UInt32 dst_imv_id)
403 {
404 bool type_supported = FALSE;
405 TNC_MessageType msg_type;
406 TNC_UInt32 msg_flags;
407 enumerator_t *enumerator;
408 imv_t *imv;
409
410 msg_type = (msg_vid << 8) | msg_subtype;
411
412 this->lock->read_lock(this->lock);
413 enumerator = this->imvs->create_enumerator(this->imvs);
414 while (enumerator->enumerate(enumerator, &imv))
415 {
416 if (imv->type_supported(imv, msg_vid, msg_subtype) &&
417 (!excl || (excl && imv->has_id(imv, dst_imv_id))))
418 {
419 if (imv->receive_message_long && src_imc_id)
420 {
421 type_supported = TRUE;
422 msg_flags = excl ? TNC_MESSAGE_FLAGS_EXCLUSIVE : 0;
423 imv->receive_message_long(imv->get_id(imv), connection_id,
424 msg_flags, msg, msg_len, msg_vid, msg_subtype,
425 src_imc_id, dst_imv_id);
426
427 }
428 else if (imv->receive_message && msg_vid <= TNC_VENDORID_ANY &&
429 msg_subtype <= TNC_SUBTYPE_ANY)
430 {
431 type_supported = TRUE;
432 msg_type = (msg_vid << 8) | msg_subtype;
433 imv->receive_message(imv->get_id(imv), connection_id,
434 msg, msg_len, msg_type);
435 }
436 }
437 }
438 enumerator->destroy(enumerator);
439 this->lock->unlock(this->lock);
440
441 if (!type_supported)
442 {
443 DBG2(DBG_TNC, "message type 0x%06x/0x%08x not supported by any IMV",
444 msg_vid, msg_subtype);
445 }
446 }
447
448 METHOD(imv_manager_t, batch_ending, void,
449 private_tnc_imv_manager_t *this, TNC_ConnectionID id)
450 {
451 enumerator_t *enumerator;
452 imv_t *imv;
453
454 this->lock->read_lock(this->lock);
455 enumerator = this->imvs->create_enumerator(this->imvs);
456 while (enumerator->enumerate(enumerator, &imv))
457 {
458 if (imv->batch_ending)
459 {
460 imv->batch_ending(imv->get_id(imv), id);
461 }
462 }
463 enumerator->destroy(enumerator);
464 this->lock->unlock(this->lock);
465 }
466
467 METHOD(imv_manager_t, destroy, void,
468 private_tnc_imv_manager_t *this)
469 {
470 imv_t *imv;
471
472 while (this->imvs->remove_last(this->imvs, (void**)&imv) == SUCCESS)
473 {
474 if (imv->terminate &&
475 imv->terminate(imv->get_id(imv)) != TNC_RESULT_SUCCESS)
476 {
477 DBG1(DBG_TNC, "IMV \"%s\" not terminated successfully",
478 imv->get_name(imv));
479 }
480 imv->destroy(imv);
481 }
482 this->imvs->destroy(this->imvs);
483 this->lock->destroy(this->lock);
484 this->id_mutex->destroy(this->id_mutex);
485 free(this);
486 }
487
488 /**
489 * Described in header.
490 */
491 imv_manager_t* tnc_imv_manager_create(void)
492 {
493 private_tnc_imv_manager_t *this;
494 recommendation_policy_t policy;
495
496 INIT(this,
497 .public = {
498 .add = _add,
499 .remove = _remove_, /* avoid name conflict with stdio.h */
500 .load = _load,
501 .load_from_functions = _load_from_functions,
502 .is_registered = _is_registered,
503 .reserve_id = _reserve_id,
504 .get_recommendation_policy = _get_recommendation_policy,
505 .create_recommendations = _create_recommendations,
506 .enforce_recommendation = _enforce_recommendation,
507 .notify_connection_change = _notify_connection_change,
508 .set_message_types = _set_message_types,
509 .set_message_types_long = _set_message_types_long,
510 .solicit_recommendation = _solicit_recommendation,
511 .receive_message = _receive_message,
512 .batch_ending = _batch_ending,
513 .destroy = _destroy,
514 },
515 .imvs = linked_list_create(),
516 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
517 .id_mutex = mutex_create(MUTEX_TYPE_DEFAULT),
518 .next_imv_id = 1,
519 );
520
521 policy = enum_from_name(recommendation_policy_names,
522 lib->settings->get_str(lib->settings,
523 "%s.plugins.tnc-imv.recommendation_policy", "default",
524 charon->name));
525 this->policy = (policy != -1) ? policy : RECOMMENDATION_POLICY_DEFAULT;
526 DBG1(DBG_TNC, "TNC recommendation policy is '%N'",
527 recommendation_policy_names, this->policy);
528
529 return &this->public;
530 }