db72011e28f55f0a3105e3098e579dac9634175a
[strongswan.git] / src / libcharon / tnc / tnccs / tnccs_manager.c
1 /*
2 * Copyright (C) 2010 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 #define USE_TNC
17
18 #include "tnccs_manager.h"
19
20 #include <tnc/imv/imv_recommendations.h>
21
22 #include <debug.h>
23 #include <daemon.h>
24 #include <utils/linked_list.h>
25 #include <threading/rwlock.h>
26
27 typedef struct private_tnccs_manager_t private_tnccs_manager_t;
28 typedef struct tnccs_entry_t tnccs_entry_t;
29 typedef struct tnccs_connection_entry_t tnccs_connection_entry_t;
30
31 /**
32 * TNCCS constructor entry
33 */
34 struct tnccs_entry_t {
35
36 /**
37 * TNCCS protocol type
38 */
39 tnccs_type_t type;
40
41 /**
42 * constructor function to create instance
43 */
44 tnccs_constructor_t constructor;
45 };
46
47 /**
48 * TNCCS connection entry
49 */
50 struct tnccs_connection_entry_t {
51
52 /**
53 * TNCCS connection ID
54 */
55 TNC_ConnectionID id;
56
57 /**
58 * TNCCS instance
59 */
60 tnccs_t *tnccs;
61
62 /**
63 * TNCCS send message function
64 */
65 tnccs_send_message_t send_message;
66
67 /**
68 * TNCCS request handshake retry flag
69 */
70 bool *request_handshake_retry;
71
72 /**
73 * collection of IMV recommendations
74 */
75 recommendations_t *recs;
76 };
77
78 /**
79 * private data of tnccs_manager
80 */
81 struct private_tnccs_manager_t {
82
83 /**
84 * public functions
85 */
86 tnccs_manager_t public;
87
88 /**
89 * list of TNCCS protocol entries
90 */
91 linked_list_t *protocols;
92
93 /**
94 * rwlock to lock the TNCCS protocol entries
95 */
96 rwlock_t *protocol_lock;
97
98 /**
99 * connection ID counter
100 */
101 TNC_ConnectionID connection_id;
102
103 /**
104 * list of TNCCS connection entries
105 */
106 linked_list_t *connections;
107
108 /**
109 * rwlock to lock TNCCS connection entries
110 */
111 rwlock_t *connection_lock;
112
113 };
114
115 METHOD(tnccs_manager_t, add_method, void,
116 private_tnccs_manager_t *this, tnccs_type_t type,
117 tnccs_constructor_t constructor)
118 {
119 tnccs_entry_t *entry;
120
121 entry = malloc_thing(tnccs_entry_t);
122 entry->type = type;
123 entry->constructor = constructor;
124
125 this->protocol_lock->write_lock(this->protocol_lock);
126 this->protocols->insert_last(this->protocols, entry);
127 this->protocol_lock->unlock(this->protocol_lock);
128 }
129
130 METHOD(tnccs_manager_t, remove_method, void,
131 private_tnccs_manager_t *this, tnccs_constructor_t constructor)
132 {
133 enumerator_t *enumerator;
134 tnccs_entry_t *entry;
135
136 this->protocol_lock->write_lock(this->protocol_lock);
137 enumerator = this->protocols->create_enumerator(this->protocols);
138 while (enumerator->enumerate(enumerator, &entry))
139 {
140 if (constructor == entry->constructor)
141 {
142 this->protocols->remove_at(this->protocols, enumerator);
143 free(entry);
144 }
145 }
146 enumerator->destroy(enumerator);
147 this->protocol_lock->unlock(this->protocol_lock);
148 }
149
150 METHOD(tnccs_manager_t, create_instance, tnccs_t*,
151 private_tnccs_manager_t *this, tnccs_type_t type, bool is_server)
152 {
153 enumerator_t *enumerator;
154 tnccs_entry_t *entry;
155 tnccs_t *protocol = NULL;
156
157 this->protocol_lock->read_lock(this->protocol_lock);
158 enumerator = this->protocols->create_enumerator(this->protocols);
159 while (enumerator->enumerate(enumerator, &entry))
160 {
161 if (type == entry->type)
162 {
163 protocol = entry->constructor(is_server);
164 if (protocol)
165 {
166 break;
167 }
168 }
169 }
170 enumerator->destroy(enumerator);
171 this->protocol_lock->unlock(this->protocol_lock);
172
173 return protocol;
174 }
175
176 METHOD(tnccs_manager_t, create_connection, TNC_ConnectionID,
177 private_tnccs_manager_t *this, tnccs_t *tnccs,
178 tnccs_send_message_t send_message, bool* request_handshake_retry,
179 recommendations_t **recs)
180 {
181 tnccs_connection_entry_t *entry;
182
183 entry = malloc_thing(tnccs_connection_entry_t);
184 entry->tnccs = tnccs;
185 entry->send_message = send_message;
186 entry->request_handshake_retry = request_handshake_retry;
187 if (recs)
188 {
189 /* we assume a TNC Server needing recommendations from IMVs */
190 if (!charon->imvs)
191 {
192 DBG1(DBG_TNC, "no IMV manager available!");
193 free(entry);
194 return 0;
195 }
196 entry->recs = charon->imvs->create_recommendations(charon->imvs);
197 *recs = entry->recs;
198 }
199 else
200 {
201 /* we assume a TNC Client */
202 if (!charon->imcs)
203 {
204 DBG1(DBG_TNC, "no IMC manager available!");
205 free(entry);
206 return 0;
207 }
208 entry->recs = NULL;
209 }
210 this->connection_lock->write_lock(this->connection_lock);
211 entry->id = ++this->connection_id;
212 this->connections->insert_last(this->connections, entry);
213 this->connection_lock->unlock(this->connection_lock);
214
215 DBG1(DBG_TNC, "assigned TNCCS Connection ID %u", entry->id);
216 return entry->id;
217 }
218
219 METHOD(tnccs_manager_t, remove_connection, void,
220 private_tnccs_manager_t *this, TNC_ConnectionID id, bool is_server)
221 {
222 enumerator_t *enumerator;
223 tnccs_connection_entry_t *entry;
224
225 if (is_server)
226 {
227 if (charon->imvs)
228 {
229 charon->imvs->notify_connection_change(charon->imvs, id,
230 TNC_CONNECTION_STATE_DELETE);
231 }
232 }
233 else
234 {
235 if (charon->imcs)
236 {
237 charon->imcs->notify_connection_change(charon->imcs, id,
238 TNC_CONNECTION_STATE_DELETE);
239 }
240 }
241
242 this->connection_lock->write_lock(this->connection_lock);
243 enumerator = this->connections->create_enumerator(this->connections);
244 while (enumerator->enumerate(enumerator, &entry))
245 {
246 if (id == entry->id)
247 {
248 this->connections->remove_at(this->connections, enumerator);
249 if (entry->recs)
250 {
251 entry->recs->destroy(entry->recs);
252 }
253 free(entry);
254 DBG1(DBG_TNC, "removed TNCCS Connection ID %u", id);
255 }
256 }
257 enumerator->destroy(enumerator);
258 this->connection_lock->unlock(this->connection_lock);
259 }
260
261 METHOD(tnccs_manager_t, request_handshake_retry, TNC_Result,
262 private_tnccs_manager_t *this, bool is_imc, TNC_UInt32 imcv_id,
263 TNC_ConnectionID id,
264 TNC_RetryReason reason)
265 {
266 enumerator_t *enumerator;
267 tnccs_connection_entry_t *entry;
268
269 if (id == TNC_CONNECTIONID_ANY)
270 {
271 DBG2(DBG_TNC, "%s %u requests handshake retry for all connections "
272 "(reason: %u)", is_imc ? "IMC":"IMV", reason);
273 }
274 else
275 {
276 DBG2(DBG_TNC, "%s %u requests handshake retry for Connection ID %u "
277 "(reason: %u)", is_imc ? "IMC":"IMV", imcv_id, id, reason);
278 }
279 this->connection_lock->read_lock(this->connection_lock);
280 enumerator = this->connections->create_enumerator(this->connections);
281 while (enumerator->enumerate(enumerator, &entry))
282 {
283 if (id == TNC_CONNECTIONID_ANY || id == entry->id)
284 {
285 *entry->request_handshake_retry = TRUE;
286 break;
287 }
288 }
289 enumerator->destroy(enumerator);
290 this->connection_lock->unlock(this->connection_lock);
291
292 return TNC_RESULT_SUCCESS;
293 }
294
295 METHOD(tnccs_manager_t, send_message, TNC_Result,
296 private_tnccs_manager_t *this, TNC_IMCID imc_id, TNC_IMVID imv_id,
297 TNC_ConnectionID id,
298 TNC_BufferReference msg,
299 TNC_UInt32 msg_len,
300 TNC_MessageType msg_type)
301
302 {
303 enumerator_t *enumerator;
304 tnccs_connection_entry_t *entry;
305 tnccs_send_message_t send_message = NULL;
306 tnccs_t *tnccs = NULL;
307 TNC_VendorID msg_vid;
308 TNC_MessageSubtype msg_subtype;
309
310 msg_vid = (msg_type >> 8) & TNC_VENDORID_ANY;
311 msg_subtype = msg_type & TNC_SUBTYPE_ANY;
312
313 if (msg_vid == TNC_VENDORID_ANY || msg_subtype == TNC_SUBTYPE_ANY)
314 {
315 DBG1(DBG_TNC, "not sending message of invalid type 0x%08x", msg_type);
316 return TNC_RESULT_INVALID_PARAMETER;
317 }
318
319 this->connection_lock->read_lock(this->connection_lock);
320 enumerator = this->connections->create_enumerator(this->connections);
321 while (enumerator->enumerate(enumerator, &entry))
322 {
323 if (id == entry->id)
324 {
325 tnccs = entry->tnccs;
326 send_message = entry->send_message;
327 break;
328 }
329 }
330 enumerator->destroy(enumerator);
331 this->connection_lock->unlock(this->connection_lock);
332
333 if (tnccs && send_message)
334 {
335 return send_message(tnccs, imc_id, imv_id, msg, msg_len, msg_type);
336 }
337 return TNC_RESULT_FATAL;
338 }
339
340 METHOD(tnccs_manager_t, provide_recommendation, TNC_Result,
341 private_tnccs_manager_t *this, TNC_IMVID imv_id,
342 TNC_ConnectionID id,
343 TNC_IMV_Action_Recommendation rec,
344 TNC_IMV_Evaluation_Result eval)
345 {
346 enumerator_t *enumerator;
347 tnccs_connection_entry_t *entry;
348 recommendations_t *recs = NULL;
349
350 this->connection_lock->read_lock(this->connection_lock);
351 enumerator = this->connections->create_enumerator(this->connections);
352 while (enumerator->enumerate(enumerator, &entry))
353 {
354 if (id == entry->id)
355 {
356 recs = entry->recs;
357 break;
358 }
359 }
360 enumerator->destroy(enumerator);
361 this->connection_lock->unlock(this->connection_lock);
362
363 if (recs)
364 {
365 recs->provide_recommendation(recs, imv_id, rec, eval);
366 return TNC_RESULT_SUCCESS;
367 }
368 return TNC_RESULT_FATAL;
369 }
370
371 METHOD(tnccs_manager_t, get_attribute, TNC_Result,
372 private_tnccs_manager_t *this, TNC_IMVID imv_id,
373 TNC_ConnectionID id,
374 TNC_AttributeID attribute_id,
375 TNC_UInt32 buffer_len,
376 TNC_BufferReference buffer,
377 TNC_UInt32 *out_value_len)
378 {
379 enumerator_t *enumerator;
380 tnccs_connection_entry_t *entry;
381 recommendations_t *recs = NULL;
382
383 if (id == TNC_CONNECTIONID_ANY ||
384 attribute_id != TNC_ATTRIBUTEID_PREFERRED_LANGUAGE)
385 {
386 return TNC_RESULT_INVALID_PARAMETER;
387 }
388
389 this->connection_lock->read_lock(this->connection_lock);
390 enumerator = this->connections->create_enumerator(this->connections);
391 while (enumerator->enumerate(enumerator, &entry))
392 {
393 if (id == entry->id)
394 {
395 recs = entry->recs;
396 break;
397 }
398 }
399 enumerator->destroy(enumerator);
400 this->connection_lock->unlock(this->connection_lock);
401
402 if (recs)
403 {
404 chunk_t pref_lang;
405
406 pref_lang = recs->get_preferred_language(recs);
407 if (pref_lang.len == 0)
408 {
409 return TNC_RESULT_INVALID_PARAMETER;
410 }
411 *out_value_len = pref_lang.len;
412 if (buffer && buffer_len >= pref_lang.len)
413 {
414 memcpy(buffer, pref_lang.ptr, pref_lang.len);
415 }
416 return TNC_RESULT_SUCCESS;
417 }
418 return TNC_RESULT_INVALID_PARAMETER;
419 }
420
421 METHOD(tnccs_manager_t, set_attribute, TNC_Result,
422 private_tnccs_manager_t *this, TNC_IMVID imv_id,
423 TNC_ConnectionID id,
424 TNC_AttributeID attribute_id,
425 TNC_UInt32 buffer_len,
426 TNC_BufferReference buffer)
427 {
428 enumerator_t *enumerator;
429 tnccs_connection_entry_t *entry;
430 recommendations_t *recs = NULL;
431
432 if (id == TNC_CONNECTIONID_ANY ||
433 (attribute_id != TNC_ATTRIBUTEID_REASON_STRING &&
434 attribute_id != TNC_ATTRIBUTEID_REASON_LANGUAGE))
435 {
436 return TNC_RESULT_INVALID_PARAMETER;
437 }
438
439 this->connection_lock->read_lock(this->connection_lock);
440 enumerator = this->connections->create_enumerator(this->connections);
441 while (enumerator->enumerate(enumerator, &entry))
442 {
443 if (id == entry->id)
444 {
445 recs = entry->recs;
446 break;
447 }
448 }
449 enumerator->destroy(enumerator);
450 this->connection_lock->unlock(this->connection_lock);
451
452 if (recs)
453 {
454 chunk_t attribute = { buffer, buffer_len };
455
456 if (attribute_id == TNC_ATTRIBUTEID_REASON_STRING)
457 {
458 return recs->set_reason_string(recs, imv_id, attribute);
459 }
460 else
461 {
462 return recs->set_reason_language(recs, imv_id, attribute);
463 }
464 }
465 return TNC_RESULT_INVALID_PARAMETER;
466 }
467
468 METHOD(tnccs_manager_t, destroy, void,
469 private_tnccs_manager_t *this)
470 {
471 this->protocols->destroy_function(this->protocols, free);
472 this->protocol_lock->destroy(this->protocol_lock);
473 this->connections->destroy_function(this->connections, free);
474 this->connection_lock->destroy(this->connection_lock);
475 free(this);
476 }
477
478 /*
479 * See header
480 */
481 tnccs_manager_t *tnccs_manager_create()
482 {
483 private_tnccs_manager_t *this;
484
485 INIT(this,
486 .public = {
487 .add_method = _add_method,
488 .remove_method = _remove_method,
489 .create_instance = _create_instance,
490 .create_connection = _create_connection,
491 .remove_connection = _remove_connection,
492 .request_handshake_retry = _request_handshake_retry,
493 .send_message = _send_message,
494 .provide_recommendation = _provide_recommendation,
495 .get_attribute = _get_attribute,
496 .set_attribute = _set_attribute,
497 .destroy = _destroy,
498 },
499 .protocols = linked_list_create(),
500 .connections = linked_list_create(),
501 .protocol_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
502 .connection_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
503 );
504
505 return &this->public;
506 }
507