6a4a0384eba432ba0c5b261432c0a3f0decbbee6
[strongswan.git] / src / libcharon / plugins / eap_radius / eap_radius_plugin.c
1 /*
2 * Copyright (C) 2013 Tobias Brunner
3 * Copyright (C) 2009 Martin Willi
4 * 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 "eap_radius_plugin.h"
18
19 #include "eap_radius.h"
20 #include "eap_radius_xauth.h"
21 #include "eap_radius_accounting.h"
22 #include "eap_radius_dae.h"
23 #include "eap_radius_forward.h"
24 #include "eap_radius_provider.h"
25
26 #include <radius_client.h>
27 #include <radius_config.h>
28
29 #include <daemon.h>
30 #include <threading/rwlock.h>
31 #include <processing/jobs/callback_job.h>
32 #include <processing/jobs/delete_ike_sa_job.h>
33
34 /**
35 * Default RADIUS server port for authentication
36 */
37 #define AUTH_PORT 1812
38
39 /**
40 * Default RADIUS server port for accounting
41 */
42 #define ACCT_PORT 1813
43
44 typedef struct private_eap_radius_plugin_t private_eap_radius_plugin_t;
45
46 /**
47 * Private data of an eap_radius_plugin_t object.
48 */
49 struct private_eap_radius_plugin_t {
50
51 /**
52 * Public radius_plugin_t interface.
53 */
54 eap_radius_plugin_t public;
55
56 /**
57 * List of RADIUS server configurations
58 */
59 linked_list_t *configs;
60
61 /**
62 * Lock for configs list
63 */
64 rwlock_t *lock;
65
66 /**
67 * RADIUS sessions for accounting
68 */
69 eap_radius_accounting_t *accounting;
70
71 /**
72 * IKE attribute provider
73 */
74 eap_radius_provider_t *provider;
75
76 /**
77 * Dynamic authorization extensions
78 */
79 eap_radius_dae_t *dae;
80
81 /**
82 * RADIUS <-> IKE attribute forwarding
83 */
84 eap_radius_forward_t *forward;
85 };
86
87 /**
88 * Instance of the EAP plugin
89 */
90 static private_eap_radius_plugin_t *instance = NULL;
91
92 /**
93 * Load RADIUS servers from configuration
94 */
95 static void load_configs(private_eap_radius_plugin_t *this)
96 {
97 enumerator_t *enumerator;
98 radius_config_t *config;
99 char *nas_identifier, *secret, *address, *section;
100 int auth_port, acct_port, sockets, preference;
101
102 address = lib->settings->get_str(lib->settings,
103 "%s.plugins.eap-radius.server", NULL, lib->ns);
104 if (address)
105 { /* legacy configuration */
106 secret = lib->settings->get_str(lib->settings,
107 "%s.plugins.eap-radius.secret", NULL, lib->ns);
108 if (!secret)
109 {
110 DBG1(DBG_CFG, "no RADIUS secret defined");
111 return;
112 }
113 nas_identifier = lib->settings->get_str(lib->settings,
114 "%s.plugins.eap-radius.nas_identifier", "strongSwan",
115 lib->ns);
116 auth_port = lib->settings->get_int(lib->settings,
117 "%s.plugins.eap-radius.port", AUTH_PORT, lib->ns);
118 sockets = lib->settings->get_int(lib->settings,
119 "%s.plugins.eap-radius.sockets", 1, lib->ns);
120 config = radius_config_create(address, address, auth_port, ACCT_PORT,
121 nas_identifier, secret, sockets, 0);
122 if (!config)
123 {
124 DBG1(DBG_CFG, "no RADUIS server defined");
125 return;
126 }
127 this->configs->insert_last(this->configs, config);
128 return;
129 }
130
131 enumerator = lib->settings->create_section_enumerator(lib->settings,
132 "%s.plugins.eap-radius.servers", lib->ns);
133 while (enumerator->enumerate(enumerator, &section))
134 {
135 address = lib->settings->get_str(lib->settings,
136 "%s.plugins.eap-radius.servers.%s.address", NULL,
137 lib->ns, section);
138 if (!address)
139 {
140 DBG1(DBG_CFG, "RADIUS server '%s' misses address, skipped", section);
141 continue;
142 }
143 secret = lib->settings->get_str(lib->settings,
144 "%s.plugins.eap-radius.servers.%s.secret", NULL,
145 lib->ns, section);
146 if (!secret)
147 {
148 DBG1(DBG_CFG, "RADIUS server '%s' misses secret, skipped", section);
149 continue;
150 }
151 nas_identifier = lib->settings->get_str(lib->settings,
152 "%s.plugins.eap-radius.servers.%s.nas_identifier",
153 lib->settings->get_str(lib->settings,
154 "%s.plugins.eap-radius.nas_identifier", "strongSwan",
155 lib->ns),
156 lib->ns, section);
157 auth_port = lib->settings->get_int(lib->settings,
158 "%s.plugins.eap-radius.servers.%s.auth_port",
159 lib->settings->get_int(lib->settings,
160 "%s.plugins.eap-radius.servers.%s.port",
161 lib->settings->get_int(lib->settings,
162 "%s.plugins.eap-radius.port", AUTH_PORT, lib->ns),
163 lib->ns, section),
164 lib->ns, section);
165 acct_port = lib->settings->get_int(lib->settings,
166 "%s.plugins.eap-radius.servers.%s.acct_port", ACCT_PORT,
167 lib->ns, section);
168 sockets = lib->settings->get_int(lib->settings,
169 "%s.plugins.eap-radius.servers.%s.sockets",
170 lib->settings->get_int(lib->settings,
171 "%s.plugins.eap-radius.sockets", 1, lib->ns),
172 lib->ns, section);
173 preference = lib->settings->get_int(lib->settings,
174 "%s.plugins.eap-radius.servers.%s.preference", 0,
175 lib->ns, section);
176 config = radius_config_create(section, address, auth_port, acct_port,
177 nas_identifier, secret, sockets, preference);
178 if (!config)
179 {
180 DBG1(DBG_CFG, "loading RADIUS server '%s' failed, skipped", section);
181 continue;
182 }
183 this->configs->insert_last(this->configs, config);
184 }
185 enumerator->destroy(enumerator);
186
187 DBG1(DBG_CFG, "loaded %d RADIUS server configuration%s",
188 this->configs->get_count(this->configs),
189 this->configs->get_count(this->configs) == 1 ? "" : "s");
190 }
191
192 METHOD(plugin_t, get_name, char*,
193 private_eap_radius_plugin_t *this)
194 {
195 return "eap-radius";
196 }
197
198 /**
199 * Register listener
200 */
201 static bool plugin_cb(private_eap_radius_plugin_t *this,
202 plugin_feature_t *feature, bool reg, void *cb_data)
203 {
204 if (reg)
205 {
206 this->accounting = eap_radius_accounting_create();
207 this->forward = eap_radius_forward_create();
208 this->provider = eap_radius_provider_create();
209
210 load_configs(this);
211
212 if (lib->settings->get_bool(lib->settings,
213 "%s.plugins.eap-radius.dae.enable", FALSE, lib->ns))
214 {
215 this->dae = eap_radius_dae_create(this->accounting);
216 }
217 if (this->forward)
218 {
219 charon->bus->add_listener(charon->bus, &this->forward->listener);
220 }
221 charon->attributes->add_provider(charon->attributes,
222 &this->provider->provider);
223 }
224 else
225 {
226 charon->attributes->remove_provider(charon->attributes,
227 &this->provider->provider);
228 if (this->forward)
229 {
230 charon->bus->remove_listener(charon->bus, &this->forward->listener);
231 this->forward->destroy(this->forward);
232 }
233 DESTROY_IF(this->dae);
234 this->provider->destroy(this->provider);
235 this->accounting->destroy(this->accounting);
236 }
237 return TRUE;
238 }
239
240 METHOD(plugin_t, get_features, int,
241 private_eap_radius_plugin_t *this, plugin_feature_t *features[])
242 {
243 static plugin_feature_t f[] = {
244 PLUGIN_CALLBACK(eap_method_register, eap_radius_create),
245 PLUGIN_PROVIDE(EAP_SERVER, EAP_RADIUS),
246 PLUGIN_DEPENDS(CUSTOM, "eap-radius"),
247 PLUGIN_CALLBACK(xauth_method_register, eap_radius_xauth_create_server),
248 PLUGIN_PROVIDE(XAUTH_SERVER, "radius"),
249 PLUGIN_DEPENDS(CUSTOM, "eap-radius"),
250 PLUGIN_CALLBACK((plugin_feature_callback_t)plugin_cb, NULL),
251 PLUGIN_PROVIDE(CUSTOM, "eap-radius"),
252 PLUGIN_DEPENDS(HASHER, HASH_MD5),
253 PLUGIN_DEPENDS(SIGNER, AUTH_HMAC_MD5_128),
254 PLUGIN_DEPENDS(RNG, RNG_WEAK),
255 };
256 *features = f;
257 return countof(f);
258 }
259
260 METHOD(plugin_t, reload, bool,
261 private_eap_radius_plugin_t *this)
262 {
263 this->lock->write_lock(this->lock);
264 this->configs->destroy_offset(this->configs,
265 offsetof(radius_config_t, destroy));
266 this->configs = linked_list_create();
267 load_configs(this);
268 this->lock->unlock(this->lock);
269 return TRUE;
270 }
271
272 METHOD(plugin_t, destroy, void,
273 private_eap_radius_plugin_t *this)
274 {
275 this->configs->destroy_offset(this->configs,
276 offsetof(radius_config_t, destroy));
277 this->lock->destroy(this->lock);
278 free(this);
279 instance = NULL;
280 }
281
282 /*
283 * see header file
284 */
285 plugin_t *eap_radius_plugin_create()
286 {
287 private_eap_radius_plugin_t *this;
288
289 INIT(this,
290 .public = {
291 .plugin = {
292 .get_name = _get_name,
293 .get_features = _get_features,
294 .reload = _reload,
295 .destroy = _destroy,
296 },
297 },
298 .configs = linked_list_create(),
299 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
300 );
301 instance = this;
302
303 return &this->public.plugin;
304 }
305
306 /**
307 * See header
308 */
309 radius_client_t *eap_radius_create_client()
310 {
311 if (instance)
312 {
313 enumerator_t *enumerator;
314 radius_config_t *config, *selected = NULL;
315 int current, best = -1;
316
317 instance->lock->read_lock(instance->lock);
318 enumerator = instance->configs->create_enumerator(instance->configs);
319 while (enumerator->enumerate(enumerator, &config))
320 {
321 current = config->get_preference(config);
322 if (current > best ||
323 /* for two with equal preference, 50-50 chance */
324 (current == best && random() % 2 == 0))
325 {
326 DBG2(DBG_CFG, "RADIUS server '%s' is candidate: %d",
327 config->get_name(config), current);
328 best = current;
329 DESTROY_IF(selected);
330 selected = config->get_ref(config);
331 }
332 else
333 {
334 DBG2(DBG_CFG, "RADIUS server '%s' skipped: %d",
335 config->get_name(config), current);
336 }
337 }
338 enumerator->destroy(enumerator);
339 instance->lock->unlock(instance->lock);
340
341 if (selected)
342 {
343 return radius_client_create(selected);
344 }
345 }
346 return NULL;
347 }
348
349 /**
350 * Job to delete all active IKE_SAs
351 */
352 static job_requeue_t delete_all_async(void *data)
353 {
354 enumerator_t *enumerator;
355 ike_sa_t *ike_sa;
356
357 enumerator = charon->ike_sa_manager->create_enumerator(
358 charon->ike_sa_manager, TRUE);
359 while (enumerator->enumerate(enumerator, &ike_sa))
360 {
361 lib->processor->queue_job(lib->processor,
362 (job_t*)delete_ike_sa_job_create(ike_sa->get_id(ike_sa), TRUE));
363 }
364 enumerator->destroy(enumerator);
365
366 return JOB_REQUEUE_NONE;
367 }
368
369 /**
370 * See header.
371 */
372 void eap_radius_handle_timeout(ike_sa_id_t *id)
373 {
374 charon->bus->alert(charon->bus, ALERT_RADIUS_NOT_RESPONDING);
375
376 if (lib->settings->get_bool(lib->settings,
377 "%s.plugins.eap-radius.close_all_on_timeout",
378 FALSE, lib->ns))
379 {
380 DBG1(DBG_CFG, "deleting all IKE_SAs after RADIUS timeout");
381 lib->processor->queue_job(lib->processor,
382 (job_t*)callback_job_create_with_prio(
383 (callback_job_cb_t)delete_all_async, NULL, NULL,
384 (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
385 }
386 else if (id)
387 {
388 DBG1(DBG_CFG, "deleting IKE_SA after RADIUS timeout");
389 lib->processor->queue_job(lib->processor,
390 (job_t*)delete_ike_sa_job_create(id, TRUE));
391 }
392 }