added TNC_TNCC_SendMessageLong() and TNC_TNCS_SendMessageLong() functions
[strongswan.git] / src / libcharon / plugins / tnc_tnccs / tnc_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 #include "tnc_tnccs_manager.h"
17
18 #include <tnc/tnc.h>
19 #include <tnc/imv/imv_manager.h>
20 #include <tnc/imc/imc_manager.h>
21 #include <tnc/imv/imv_manager.h>
22
23 #include <debug.h>
24 #include <utils/linked_list.h>
25 #include <threading/rwlock.h>
26
27 typedef struct private_tnc_tnccs_manager_t private_tnc_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 tnc_tnccs_manager
80 */
81 struct private_tnc_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_tnc_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_tnc_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_tnc_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_tnc_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 (!tnc->imvs)
191 {
192 DBG1(DBG_TNC, "no IMV manager available!");
193 free(entry);
194 return 0;
195 }
196 entry->recs = tnc->imvs->create_recommendations(tnc->imvs);
197 *recs = entry->recs;
198 }
199 else
200 {
201 /* we assume a TNC Client */
202 if (!tnc->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_tnc_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 (tnc->imvs)
228 {
229 tnc->imvs->notify_connection_change(tnc->imvs, id,
230 TNC_CONNECTION_STATE_DELETE);
231 }
232 }
233 else
234 {
235 if (tnc->imcs)
236 {
237 tnc->imcs->notify_connection_change(tnc->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_tnc_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_tnc_tnccs_manager_t *this, TNC_IMCID imc_id, TNC_IMVID imv_id,
297 TNC_ConnectionID id,
298 TNC_UInt32 msg_flags,
299 TNC_BufferReference msg,
300 TNC_UInt32 msg_len,
301 TNC_VendorID msg_vid,
302 TNC_MessageSubtype msg_subtype)
303
304 {
305 enumerator_t *enumerator;
306 tnccs_connection_entry_t *entry;
307 tnccs_send_message_t send_message = NULL;
308 tnccs_t *tnccs = NULL;
309
310 if (msg_vid == TNC_VENDORID_ANY || msg_subtype == TNC_SUBTYPE_ANY)
311 {
312 DBG1(DBG_TNC, "not sending message of invalid type 0x%02x/0x%08x",
313 msg_vid, msg_subtype);
314 return TNC_RESULT_INVALID_PARAMETER;
315 }
316
317 this->connection_lock->read_lock(this->connection_lock);
318 enumerator = this->connections->create_enumerator(this->connections);
319 while (enumerator->enumerate(enumerator, &entry))
320 {
321 if (id == entry->id)
322 {
323 tnccs = entry->tnccs;
324 send_message = entry->send_message;
325 break;
326 }
327 }
328 enumerator->destroy(enumerator);
329 this->connection_lock->unlock(this->connection_lock);
330
331 if (tnccs && send_message)
332 {
333 return send_message(tnccs, imc_id, imv_id, msg_flags, msg, msg_len,
334 msg_vid, msg_subtype);
335 }
336 return TNC_RESULT_FATAL;
337 }
338
339 METHOD(tnccs_manager_t, provide_recommendation, TNC_Result,
340 private_tnc_tnccs_manager_t *this, TNC_IMVID imv_id,
341 TNC_ConnectionID id,
342 TNC_IMV_Action_Recommendation rec,
343 TNC_IMV_Evaluation_Result eval)
344 {
345 enumerator_t *enumerator;
346 tnccs_connection_entry_t *entry;
347 recommendations_t *recs = NULL;
348
349 this->connection_lock->read_lock(this->connection_lock);
350 enumerator = this->connections->create_enumerator(this->connections);
351 while (enumerator->enumerate(enumerator, &entry))
352 {
353 if (id == entry->id)
354 {
355 recs = entry->recs;
356 break;
357 }
358 }
359 enumerator->destroy(enumerator);
360 this->connection_lock->unlock(this->connection_lock);
361
362 if (recs)
363 {
364 recs->provide_recommendation(recs, imv_id, rec, eval);
365 return TNC_RESULT_SUCCESS;
366 }
367 return TNC_RESULT_FATAL;
368 }
369
370 METHOD(tnccs_manager_t, get_attribute, TNC_Result,
371 private_tnc_tnccs_manager_t *this, TNC_IMVID imv_id,
372 TNC_ConnectionID id,
373 TNC_AttributeID attribute_id,
374 TNC_UInt32 buffer_len,
375 TNC_BufferReference buffer,
376 TNC_UInt32 *out_value_len)
377 {
378 enumerator_t *enumerator;
379 tnccs_connection_entry_t *entry;
380 recommendations_t *recs = NULL;
381
382 if (id == TNC_CONNECTIONID_ANY ||
383 attribute_id != TNC_ATTRIBUTEID_PREFERRED_LANGUAGE)
384 {
385 return TNC_RESULT_INVALID_PARAMETER;
386 }
387
388 this->connection_lock->read_lock(this->connection_lock);
389 enumerator = this->connections->create_enumerator(this->connections);
390 while (enumerator->enumerate(enumerator, &entry))
391 {
392 if (id == entry->id)
393 {
394 recs = entry->recs;
395 break;
396 }
397 }
398 enumerator->destroy(enumerator);
399 this->connection_lock->unlock(this->connection_lock);
400
401 if (recs)
402 {
403 chunk_t pref_lang;
404
405 pref_lang = recs->get_preferred_language(recs);
406 if (pref_lang.len == 0)
407 {
408 return TNC_RESULT_INVALID_PARAMETER;
409 }
410 *out_value_len = pref_lang.len;
411 if (buffer && buffer_len >= pref_lang.len)
412 {
413 memcpy(buffer, pref_lang.ptr, pref_lang.len);
414 }
415 return TNC_RESULT_SUCCESS;
416 }
417 return TNC_RESULT_INVALID_PARAMETER;
418 }
419
420 METHOD(tnccs_manager_t, set_attribute, TNC_Result,
421 private_tnc_tnccs_manager_t *this, TNC_IMVID imv_id,
422 TNC_ConnectionID id,
423 TNC_AttributeID attribute_id,
424 TNC_UInt32 buffer_len,
425 TNC_BufferReference buffer)
426 {
427 enumerator_t *enumerator;
428 tnccs_connection_entry_t *entry;
429 recommendations_t *recs = NULL;
430
431 if (id == TNC_CONNECTIONID_ANY ||
432 (attribute_id != TNC_ATTRIBUTEID_REASON_STRING &&
433 attribute_id != TNC_ATTRIBUTEID_REASON_LANGUAGE))
434 {
435 return TNC_RESULT_INVALID_PARAMETER;
436 }
437
438 this->connection_lock->read_lock(this->connection_lock);
439 enumerator = this->connections->create_enumerator(this->connections);
440 while (enumerator->enumerate(enumerator, &entry))
441 {
442 if (id == entry->id)
443 {
444 recs = entry->recs;
445 break;
446 }
447 }
448 enumerator->destroy(enumerator);
449 this->connection_lock->unlock(this->connection_lock);
450
451 if (recs)
452 {
453 chunk_t attribute = { buffer, buffer_len };
454
455 if (attribute_id == TNC_ATTRIBUTEID_REASON_STRING)
456 {
457 return recs->set_reason_string(recs, imv_id, attribute);
458 }
459 else
460 {
461 return recs->set_reason_language(recs, imv_id, attribute);
462 }
463 }
464 return TNC_RESULT_INVALID_PARAMETER;
465 }
466
467 METHOD(tnccs_manager_t, destroy, void,
468 private_tnc_tnccs_manager_t *this)
469 {
470 this->protocols->destroy_function(this->protocols, free);
471 this->protocol_lock->destroy(this->protocol_lock);
472 this->connections->destroy_function(this->connections, free);
473 this->connection_lock->destroy(this->connection_lock);
474 free(this);
475 }
476
477 /*
478 * See header
479 */
480 tnccs_manager_t *tnc_tnccs_manager_create()
481 {
482 private_tnc_tnccs_manager_t *this;
483
484 INIT(this,
485 .public = {
486 .add_method = _add_method,
487 .remove_method = _remove_method,
488 .create_instance = _create_instance,
489 .create_connection = _create_connection,
490 .remove_connection = _remove_connection,
491 .request_handshake_retry = _request_handshake_retry,
492 .send_message = _send_message,
493 .provide_recommendation = _provide_recommendation,
494 .get_attribute = _get_attribute,
495 .set_attribute = _set_attribute,
496 .destroy = _destroy,
497 },
498 .protocols = linked_list_create(),
499 .connections = linked_list_create(),
500 .protocol_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
501 .connection_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
502 );
503
504 return &this->public;
505 }
506