Fixed a possible segfault after termination of IKE and child SAs.
[strongswan.git] / src / libcharon / control / controller.c
1 /*
2 * Copyright (C) 2007 Martin Willi
3 * 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 "controller.h"
17
18 #include <sys/types.h>
19 #include <dirent.h>
20 #include <sys/stat.h>
21 #include <dlfcn.h>
22
23 #include <daemon.h>
24 #include <library.h>
25
26
27 typedef struct private_controller_t private_controller_t;
28 typedef struct interface_listener_t interface_listener_t;
29
30 /**
31 * Private data of an stroke_t object.
32 */
33 struct private_controller_t {
34
35 /**
36 * Public part of stroke_t object.
37 */
38 controller_t public;
39 };
40
41 /**
42 * helper struct to map listener callbacks to interface callbacks
43 */
44 struct interface_listener_t {
45
46 /**
47 * public bus listener interface
48 */
49 listener_t public;
50
51 /**
52 * status of the operation, return to method callers
53 */
54 status_t status;
55
56 /**
57 * interface callback (listener gets redirected to here)
58 */
59 controller_cb_t callback;
60
61 /**
62 * user parameter to pass to callback
63 */
64 void *param;
65
66 /**
67 * child configuration, used for initiate
68 */
69 child_cfg_t *child_cfg;
70
71 /**
72 * peer configuration, used for initiate
73 */
74 peer_cfg_t *peer_cfg;
75
76 /**
77 * IKE_SA to handle
78 */
79 ike_sa_t *ike_sa;
80
81 /**
82 * CHILD_SA to handle
83 */
84 child_sa_t *child_sa;
85
86 /**
87 * unique ID, used for various methods
88 */
89 u_int32_t id;
90 };
91
92
93 typedef struct interface_job_t interface_job_t;
94
95 /**
96 * job for asynchronous listen operations
97 */
98 struct interface_job_t {
99 /**
100 * job interface
101 */
102 job_t public;
103
104 /**
105 * associated listener
106 */
107 interface_listener_t listener;
108 };
109
110 /**
111 * listener log function
112 */
113 static bool listener_log(interface_listener_t *this, debug_t group,
114 level_t level, int thread, ike_sa_t *ike_sa,
115 char* format, va_list args)
116 {
117 if (this->ike_sa == ike_sa)
118 {
119 if (!this->callback(this->param, group, level, ike_sa, format, args))
120 {
121 return FALSE;
122 }
123 }
124 return TRUE;
125 }
126
127 /**
128 * Implementation of listener_t.ike_state_change
129 */
130 static bool listener_ike_state(interface_listener_t *this, ike_sa_t *ike_sa,
131 ike_sa_state_t state)
132 {
133 if (this->ike_sa == ike_sa)
134 {
135 switch (state)
136 {
137 #ifdef ME
138 case IKE_ESTABLISHED:
139 { /* mediation connections are complete without CHILD_SA */
140 peer_cfg_t *peer_cfg = ike_sa->get_peer_cfg(ike_sa);
141
142 if (peer_cfg->is_mediation(peer_cfg))
143 {
144 this->status = SUCCESS;
145 return FALSE;
146 }
147 break;
148 }
149 #endif /* ME */
150 case IKE_DESTROYING:
151 if (ike_sa->get_state(ike_sa) == IKE_DELETING)
152 { /* proper termination */
153 this->status = SUCCESS;
154 }
155 return FALSE;
156 default:
157 break;
158 }
159 }
160 return TRUE;
161 }
162
163 /**
164 * Implementation of listener_t.child_state_change
165 */
166 static bool listener_child_state(interface_listener_t *this, ike_sa_t *ike_sa,
167 child_sa_t *child_sa, child_sa_state_t state)
168 {
169 if (this->ike_sa == ike_sa)
170 {
171 switch (state)
172 {
173 case CHILD_INSTALLED:
174 this->status = SUCCESS;
175 return FALSE;
176 case CHILD_DESTROYING:
177 switch (child_sa->get_state(child_sa))
178 {
179 case CHILD_DELETING:
180 /* proper delete */
181 this->status = SUCCESS;
182 break;
183 default:
184 break;
185 }
186 return FALSE;
187 default:
188 break;
189 }
190 }
191 return TRUE;
192 }
193
194 /**
195 * cleanup job if job is never executed
196 */
197 static void recheckin(interface_job_t *job)
198 {
199 if (job->listener.ike_sa)
200 {
201 charon->ike_sa_manager->checkin(charon->ike_sa_manager,
202 job->listener.ike_sa);
203 }
204 }
205
206 /**
207 * Implementation of controller_t.create_ike_sa_iterator.
208 */
209 static enumerator_t* create_ike_sa_enumerator(controller_t *this)
210 {
211 return charon->ike_sa_manager->create_enumerator(charon->ike_sa_manager);
212 }
213
214 /**
215 * execute function for initiate
216 */
217 static status_t initiate_execute(interface_job_t *job)
218 {
219 ike_sa_t *ike_sa;
220 interface_listener_t *listener = &job->listener;
221 peer_cfg_t *peer_cfg = listener->peer_cfg;
222
223 ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager,
224 peer_cfg);
225 listener->ike_sa = ike_sa;
226
227 if (ike_sa->get_peer_cfg(ike_sa) == NULL)
228 {
229 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
230 }
231 peer_cfg->destroy(peer_cfg);
232
233 if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS)
234 {
235 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
236 return SUCCESS;
237 }
238 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa);
239 return FAILED;
240 }
241
242 /**
243 * Implementation of controller_t.initiate.
244 */
245 static status_t initiate(private_controller_t *this,
246 peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
247 controller_cb_t callback, void *param)
248 {
249 interface_job_t job = {
250 .listener = {
251 .public = {
252 .log = (void*)listener_log,
253 .ike_state_change = (void*)listener_ike_state,
254 .child_state_change = (void*)listener_child_state,
255 },
256 .callback = callback,
257 .param = param,
258 .status = FAILED,
259 .child_cfg = child_cfg,
260 .peer_cfg = peer_cfg,
261 },
262 .public = {
263 .execute = (void*)initiate_execute,
264 .destroy = (void*)recheckin,
265 },
266 };
267 if (callback == NULL)
268 {
269 return initiate_execute(&job);
270 }
271 charon->bus->listen(charon->bus, &job.listener.public, (job_t*)&job);
272 return job.listener.status;
273 }
274
275 /**
276 * execute function for terminate_ike
277 */
278 static status_t terminate_ike_execute(interface_job_t *job)
279 {
280 interface_listener_t *listener = &job->listener;
281 ike_sa_t *ike_sa = listener->ike_sa;
282
283 charon->bus->set_sa(charon->bus, ike_sa);
284
285 if (ike_sa->delete(ike_sa) != DESTROY_ME)
286 {
287 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
288 /* delete failed */
289 return FAILED;
290 }
291 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa);
292 return SUCCESS;
293 }
294
295 /**
296 * Implementation of controller_t.terminate_ike.
297 */
298 static status_t terminate_ike(controller_t *this, u_int32_t unique_id,
299 controller_cb_t callback, void *param)
300 {
301 ike_sa_t *ike_sa;
302 interface_job_t job = {
303 .listener = {
304 .public = {
305 .log = (void*)listener_log,
306 .ike_state_change = (void*)listener_ike_state,
307 .child_state_change = (void*)listener_child_state,
308 },
309 .callback = callback,
310 .param = param,
311 .status = FAILED,
312 .id = unique_id,
313 },
314 .public = {
315 .execute = (void*)terminate_ike_execute,
316 .destroy = (void*)recheckin,
317 },
318 };
319
320 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
321 unique_id, FALSE);
322 if (ike_sa == NULL)
323 {
324 DBG1(DBG_IKE, "unable to terminate IKE_SA: ID %d not found", unique_id);
325 return NOT_FOUND;
326 }
327 job.listener.ike_sa = ike_sa;
328
329 if (callback == NULL)
330 {
331 return terminate_ike_execute(&job);
332 }
333 charon->bus->listen(charon->bus, &job.listener.public, (job_t*)&job);
334 /* checkin of the ike_sa happend in the thread that executed the job */
335 charon->bus->set_sa(charon->bus, NULL);
336 return job.listener.status;
337 }
338
339 /**
340 * execute function for terminate_child
341 */
342 static status_t terminate_child_execute(interface_job_t *job)
343 {
344 interface_listener_t *listener = &job->listener;
345 ike_sa_t *ike_sa = listener->ike_sa;
346 child_sa_t *child_sa = listener->child_sa;
347
348 charon->bus->set_sa(charon->bus, ike_sa);
349 if (ike_sa->delete_child_sa(ike_sa, child_sa->get_protocol(child_sa),
350 child_sa->get_spi(child_sa, TRUE)) != DESTROY_ME)
351 {
352 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
353 return SUCCESS;
354 }
355 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa);
356 return FAILED;
357 }
358
359 /**
360 * Implementation of controller_t.terminate_child.
361 */
362 static status_t terminate_child(controller_t *this, u_int32_t reqid,
363 controller_cb_t callback, void *param)
364 {
365 ike_sa_t *ike_sa;
366 child_sa_t *child_sa;
367 iterator_t *iterator;
368 interface_job_t job = {
369 .listener = {
370 .public = {
371 .log = (void*)listener_log,
372 .ike_state_change = (void*)listener_ike_state,
373 .child_state_change = (void*)listener_child_state,
374 },
375 .callback = callback,
376 .param = param,
377 .status = FAILED,
378 .id = reqid,
379 },
380 .public = {
381 .execute = (void*)terminate_child_execute,
382 .destroy = (void*)recheckin,
383 },
384 };
385
386 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
387 reqid, TRUE);
388 if (ike_sa == NULL)
389 {
390 DBG1(DBG_IKE, "unable to terminate, CHILD_SA with ID %d not found",
391 reqid);
392 return NOT_FOUND;
393 }
394 job.listener.ike_sa = ike_sa;
395
396 iterator = ike_sa->create_child_sa_iterator(ike_sa);
397 while (iterator->iterate(iterator, (void**)&child_sa))
398 {
399 if (child_sa->get_state(child_sa) != CHILD_ROUTED &&
400 child_sa->get_reqid(child_sa) == reqid)
401 {
402 break;
403 }
404 child_sa = NULL;
405 }
406 iterator->destroy(iterator);
407
408 if (child_sa == NULL)
409 {
410 DBG1(DBG_IKE, "unable to terminate, established "
411 "CHILD_SA with ID %d not found", reqid);
412 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
413 return NOT_FOUND;
414 }
415 job.listener.child_sa = child_sa;
416
417 if (callback == NULL)
418 {
419 return terminate_child_execute(&job);
420 }
421 charon->bus->listen(charon->bus, &job.listener.public, (job_t*)&job);
422 /* checkin of the ike_sa happend in the thread that executed the job */
423 charon->bus->set_sa(charon->bus, NULL);
424 return job.listener.status;
425 }
426
427 /**
428 * See header
429 */
430 bool controller_cb_empty(void *param, debug_t group, level_t level,
431 ike_sa_t *ike_sa, char *format, va_list args)
432 {
433 return TRUE;
434 }
435
436 /**
437 * Implementation of stroke_t.destroy.
438 */
439 static void destroy(private_controller_t *this)
440 {
441 free(this);
442 }
443
444 /*
445 * Described in header-file
446 */
447 controller_t *controller_create(void)
448 {
449 private_controller_t *this = malloc_thing(private_controller_t);
450
451 this->public.create_ike_sa_enumerator = (enumerator_t*(*)(controller_t*))create_ike_sa_enumerator;
452 this->public.initiate = (status_t(*)(controller_t*,peer_cfg_t*,child_cfg_t*,controller_cb_t,void*))initiate;
453 this->public.terminate_ike = (status_t(*)(controller_t*,u_int32_t,controller_cb_t, void*))terminate_ike;
454 this->public.terminate_child = (status_t(*)(controller_t*,u_int32_t,controller_cb_t, void *param))terminate_child;
455 this->public.destroy = (void (*)(controller_t*))destroy;
456
457 return &this->public;
458 }
459