initialize libstrongswan in dynamic stand-alone libimcv-based libraries
[strongswan.git] / src / libimcv / imv / imv_agent.c
1 /*
2 * Copyright (C) 2011 Andreas Steffen, HSR Hochschule fuer Technik Rapperswil
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 */
14
15 #include "imcv.h"
16 #include "imv_agent.h"
17
18 #include <debug.h>
19 #include <utils/linked_list.h>
20 #include <threading/rwlock.h>
21
22 typedef struct private_imv_agent_t private_imv_agent_t;
23
24 /**
25 * Private data of an imv_agent_t object.
26 */
27 struct private_imv_agent_t {
28
29 /**
30 * Public members of imv_agent_t
31 */
32 imv_agent_t public;
33
34 /**
35 * name of IMV
36 */
37 const char *name;
38
39 /**
40 * message type of IMV
41 */
42 TNC_MessageType type;
43
44 /**
45 * ID of IMV as assigned by TNCS
46 */
47 TNC_IMVID id;
48
49 /**
50 * list of TNCS connection entries
51 */
52 linked_list_t *connections;
53
54 /**
55 * rwlock to lock TNCS connection entries
56 */
57 rwlock_t *connection_lock;
58
59 /**
60 * Inform a TNCS about the set of message types the IMV is able to receive
61 *
62 * @param imv_id IMV ID assigned by TNCS
63 * @param supported_types list of supported message types
64 * @param type_count number of list elements
65 * @return TNC result code
66 */
67 TNC_Result (*report_message_types)(TNC_IMVID imv_id,
68 TNC_MessageTypeList supported_types,
69 TNC_UInt32 type_count);
70
71 /**
72 * Call when an IMV-IMC message is to be sent
73 *
74 * @param imv_id IMV ID assigned by TNCS
75 * @param connection_id network connection ID assigned by TNCS
76 * @param msg message to send
77 * @param msg_len message length in bytes
78 * @param msg_type message type
79 * @return TNC result code
80 */
81 TNC_Result (*send_message)(TNC_IMVID imv_id,
82 TNC_ConnectionID connection_id,
83 TNC_BufferReference msg,
84 TNC_UInt32 msg_len,
85 TNC_MessageType msg_type);
86
87 /**
88 * Deliver IMV Action Recommendation and IMV Evaluation Results to the TNCS
89 *
90 * @param imv_id IMV ID assigned by TNCS
91 # @param connection_id network connection ID assigned by TNCS
92 * @param rec IMV action recommendation
93 * @param eval IMV evaluation result
94 * @return TNC result code
95 */
96 TNC_Result (*provide_recommendation)(TNC_IMVID imv_id,
97 TNC_ConnectionID connection_id,
98 TNC_IMV_Action_Recommendation rec,
99 TNC_IMV_Evaluation_Result eval);
100
101 };
102
103 METHOD(imv_agent_t, bind_functions, TNC_Result,
104 private_imv_agent_t *this, TNC_TNCS_BindFunctionPointer bind_function)
105 {
106 if (!bind_function)
107 {
108 DBG1(DBG_IMV, "TNC server failed to provide bind function");
109 return TNC_RESULT_INVALID_PARAMETER;
110 }
111 if (bind_function(this->id, "TNC_TNCS_ReportMessageTypes",
112 (void**)&this->report_message_types) != TNC_RESULT_SUCCESS)
113 {
114 this->report_message_types = NULL;
115 }
116 if (bind_function(this->id, "TNC_TNCS_RequestHandshakeRetry",
117 (void**)&this->public.request_handshake_retry) != TNC_RESULT_SUCCESS)
118 {
119 this->public.request_handshake_retry = NULL;
120 }
121 if (bind_function(this->id, "TNC_TNCS_SendMessage",
122 (void**)&this->send_message) != TNC_RESULT_SUCCESS)
123 {
124 this->send_message = NULL;
125 }
126 if (bind_function(this->id, "TNC_TNCS_ProvideRecommendation",
127 (void**)&this->provide_recommendation) != TNC_RESULT_SUCCESS)
128 {
129 this->provide_recommendation = NULL;
130 }
131 if (bind_function(this->id, "TNC_TNCS_GetAttribute",
132 (void**)&this->public.get_attribute) != TNC_RESULT_SUCCESS)
133 {
134 this->public.get_attribute = NULL;
135 }
136 if (bind_function(this->id, "TNC_TNCS_SetAttribute",
137 (void**)&this->public.set_attribute) != TNC_RESULT_SUCCESS)
138 {
139 this->public.set_attribute = NULL;
140 }
141 DBG2(DBG_IMV, "IMV %u \"%s\" provided with bind function",
142 this->id, this->name);
143
144 if (this->report_message_types)
145 {
146 this->report_message_types(this->id, &this->type, 1);
147 }
148 return TNC_RESULT_SUCCESS;
149 }
150
151 /**
152 * finds a connection state based on its Connection ID
153 */
154 static imv_state_t* find_connection(private_imv_agent_t *this,
155 TNC_ConnectionID id)
156 {
157 enumerator_t *enumerator;
158 imv_state_t *state, *found = NULL;
159
160 this->connection_lock->read_lock(this->connection_lock);
161 enumerator = this->connections->create_enumerator(this->connections);
162 while (enumerator->enumerate(enumerator, &state))
163 {
164 if (id == state->get_connection_id(state))
165 {
166 found = state;
167 break;
168 }
169 }
170 enumerator->destroy(enumerator);
171 this->connection_lock->unlock(this->connection_lock);
172
173 return found;
174 }
175
176 /**
177 * delete a connection state with a given Connection ID
178 */
179 static bool delete_connection(private_imv_agent_t *this, TNC_ConnectionID id)
180 {
181 enumerator_t *enumerator;
182 imv_state_t *state;
183 bool found = FALSE;
184
185 this->connection_lock->write_lock(this->connection_lock);
186 enumerator = this->connections->create_enumerator(this->connections);
187 while (enumerator->enumerate(enumerator, &state))
188 {
189 if (id == state->get_connection_id(state))
190 {
191 found = TRUE;
192 state->destroy(state);
193 this->connections->remove_at(this->connections, enumerator);
194 break;
195 }
196 }
197 enumerator->destroy(enumerator);
198 this->connection_lock->unlock(this->connection_lock);
199
200 return found;
201 }
202
203 METHOD(imv_agent_t, create_state, TNC_Result,
204 private_imv_agent_t *this, imv_state_t *state)
205 {
206 TNC_ConnectionID connection_id;
207
208 connection_id = state->get_connection_id(state);
209 if (find_connection(this, connection_id))
210 {
211 DBG1(DBG_IMV, "IMV %u \"%s\" already created a state for Connection ID %u",
212 this->id, this->name, connection_id);
213 state->destroy(state);
214 return TNC_RESULT_OTHER;
215 }
216 this->connection_lock->write_lock(this->connection_lock);
217 this->connections->insert_last(this->connections, state);
218 this->connection_lock->unlock(this->connection_lock);
219 DBG2(DBG_IMV, "IMV %u \"%s\" created a state for Connection ID %u",
220 this->id, this->name, connection_id);
221 return TNC_RESULT_SUCCESS;
222 }
223
224 METHOD(imv_agent_t, delete_state, TNC_Result,
225 private_imv_agent_t *this, TNC_ConnectionID connection_id)
226 {
227 if (!delete_connection(this, connection_id))
228 {
229 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
230 this->id, this->name, connection_id);
231 return TNC_RESULT_FATAL;
232 }
233 DBG2(DBG_IMV, "IMV %u \"%s\" deleted the state of Connection ID %u",
234 this->id, this->name, connection_id);
235 return TNC_RESULT_SUCCESS;
236 }
237
238 METHOD(imv_agent_t, change_state, TNC_Result,
239 private_imv_agent_t *this, TNC_ConnectionID connection_id,
240 TNC_ConnectionState new_state)
241 {
242 imv_state_t *state;
243
244 switch (new_state)
245 {
246 case TNC_CONNECTION_STATE_HANDSHAKE:
247 case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
248 case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
249 case TNC_CONNECTION_STATE_ACCESS_NONE:
250 state = find_connection(this, connection_id);
251 if (!state)
252 {
253 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
254 this->id, this->name, connection_id);
255 return TNC_RESULT_FATAL;
256 }
257 state->change_state(state, new_state);
258 DBG2(DBG_IMV, "IMV %u \"%s\" changed state of Connection ID %u to '%N'",
259 this->id, this->name, connection_id,
260 TNC_Connection_State_names, new_state);
261 break;
262 case TNC_CONNECTION_STATE_CREATE:
263 DBG1(DBG_IMV, "state '%N' should be handled by create_state()",
264 TNC_Connection_State_names, new_state);
265 return TNC_RESULT_FATAL;
266 case TNC_CONNECTION_STATE_DELETE:
267 DBG1(DBG_IMV, "state '%N' should be handled by delete_state()",
268 TNC_Connection_State_names, new_state);
269 return TNC_RESULT_FATAL;
270 default:
271 DBG1(DBG_IMV, "IMV %u \"%s\" was notified of unknown state %u "
272 "for Connection ID %u",
273 this->id, this->name, new_state, connection_id);
274 return TNC_RESULT_INVALID_PARAMETER;
275 }
276 return TNC_RESULT_SUCCESS;
277 }
278
279 METHOD(imv_agent_t, get_state, bool,
280 private_imv_agent_t *this, TNC_ConnectionID connection_id,
281 imv_state_t **state)
282 {
283 *state = find_connection(this, connection_id);
284 if (!*state)
285 {
286 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
287 this->id, this->name, connection_id);
288 return FALSE;
289 }
290 return TRUE;
291 }
292
293 METHOD(imv_agent_t, send_message, TNC_Result,
294 private_imv_agent_t *this, TNC_ConnectionID connection_id, chunk_t msg)
295 {
296 if (!this->send_message)
297 {
298 return TNC_RESULT_FATAL;
299 }
300 return this->send_message(this->id, connection_id, msg.ptr, msg.len,
301 this->type);
302 }
303
304 METHOD(imv_agent_t, set_recommendation, TNC_Result,
305 private_imv_agent_t *this, TNC_ConnectionID connection_id,
306 TNC_IMV_Action_Recommendation rec,
307 TNC_IMV_Evaluation_Result eval)
308 {
309 imv_state_t *state;
310
311 state = find_connection(this, connection_id);
312 if (!state)
313 {
314 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
315 this->id, this->name, connection_id);
316 return TNC_RESULT_FATAL;
317 }
318 state->set_recommendation(state, rec, eval);
319 return this->provide_recommendation(this->id, connection_id, rec, eval);
320 }
321
322 METHOD(imv_agent_t, provide_recommendation, TNC_Result,
323 private_imv_agent_t *this, TNC_ConnectionID connection_id)
324 {
325 imv_state_t *state;
326 TNC_IMV_Action_Recommendation rec;
327 TNC_IMV_Evaluation_Result eval;
328
329 state = find_connection(this, connection_id);
330 if (!state)
331 {
332 DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u",
333 this->id, this->name, connection_id);
334 return TNC_RESULT_FATAL;
335 }
336 state->get_recommendation(state, &rec, &eval);
337 return this->provide_recommendation(this->id, connection_id, rec, eval);
338 }
339
340 METHOD(imv_agent_t, destroy, void,
341 private_imv_agent_t *this)
342 {
343 DBG1(DBG_IMV, "IMV %u \"%s\" terminated", this->id, this->name);
344 this->connections->destroy_offset(this->connections,
345 offsetof(imv_state_t, destroy));
346 this->connection_lock->destroy(this->connection_lock);
347 free(this);
348
349 /* decrease the reference count or terminate */
350 libimcv_deinit();
351 }
352
353 /**
354 * Described in header.
355 */
356 imv_agent_t *imv_agent_create(const char *name,
357 pen_t vendor_id, u_int32_t subtype,
358 TNC_IMVID id, TNC_Version *actual_version)
359 {
360 private_imv_agent_t *this;
361
362 /* initialize or increase the reference count */
363 if (!libimcv_init())
364 {
365 return NULL;
366 }
367
368 INIT(this,
369 .public = {
370 .bind_functions = _bind_functions,
371 .create_state = _create_state,
372 .delete_state = _delete_state,
373 .change_state = _change_state,
374 .get_state = _get_state,
375 .send_message = _send_message,
376 .set_recommendation = _set_recommendation,
377 .provide_recommendation = _provide_recommendation,
378 .destroy = _destroy,
379 },
380 .name = name,
381 .type = (vendor_id << 8) | (subtype && 0xff),
382 .id = id,
383 .connections = linked_list_create(),
384 .connection_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
385 );
386
387 *actual_version = TNC_IFIMV_VERSION_1;
388 DBG1(DBG_IMV, "IMV %u \"%s\" initialized", this->id, this->name);
389
390 return &this->public;
391 }
392
393