6cdbb3ca5f4eff1b729c00096474803dd5c1cab1
[strongswan.git] / src / libcharon / plugins / eap_radius / eap_radius_provider.c
1 /*
2 * Copyright (C) 2013 Martin Willi
3 * Copyright (C) 2013 revosec AG
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 "eap_radius_provider.h"
17
18 #include <daemon.h>
19 #include <collections/hashtable.h>
20 #include <threading/mutex.h>
21
22 typedef struct private_eap_radius_provider_t private_eap_radius_provider_t;
23 typedef struct private_listener_t private_listener_t;
24
25 /**
26 * Private data of registered listener
27 */
28 struct private_listener_t {
29
30 /**
31 * Implements listener_t interface
32 */
33 listener_t public;
34
35 /**
36 * Leases not acquired yet, identification_t => entry_t
37 */
38 hashtable_t *unclaimed;
39
40 /**
41 * Leases acquired, identification_t => entry_t
42 */
43 hashtable_t *claimed;
44
45 /**
46 * Mutex to lock leases
47 */
48 mutex_t *mutex;
49 };
50
51 /**
52 * Private data of an eap_radius_provider_t object.
53 */
54 struct private_eap_radius_provider_t {
55
56 /**
57 * Public eap_radius_provider_t interface.
58 */
59 eap_radius_provider_t public;
60
61 /**
62 * Additionally implements the listener_t interface
63 */
64 private_listener_t listener;
65 };
66
67 /**
68 * Singleton instance of provider
69 */
70 static eap_radius_provider_t *singleton = NULL;
71
72 /**
73 * Hashtable entry with leases
74 */
75 typedef struct {
76 /** identity we assigned the IP lease */
77 identification_t *id;
78 /** list of IP leases received from AAA, as host_t */
79 linked_list_t *addrs;
80 } entry_t;
81
82 /**
83 * destroy an entry_t
84 */
85 static void destroy_entry(entry_t *this)
86 {
87 this->id->destroy(this->id);
88 this->addrs->destroy_offset(this->addrs, offsetof(host_t, destroy));
89 free(this);
90 }
91
92 /**
93 * Get or create an entry from a locked hashtable
94 */
95 static entry_t* get_or_create_entry(hashtable_t *hashtable, identification_t *id)
96 {
97 entry_t *entry;
98
99 entry = hashtable->get(hashtable, id);
100 if (!entry)
101 {
102 INIT(entry,
103 .id = id->clone(id),
104 .addrs = linked_list_create(),
105 );
106 hashtable->put(hashtable, entry->id, entry);
107 }
108 return entry;
109 }
110
111 /**
112 * Put an entry to hashtable, or destroy it ife empty
113 */
114 static void put_or_destroy_entry(hashtable_t *hashtable, entry_t *entry)
115 {
116 if (entry->addrs->get_count(entry->addrs) > 0)
117 {
118 hashtable->put(hashtable, entry->id, entry);
119 }
120 else
121 {
122 destroy_entry(entry);
123 }
124 }
125
126 /**
127 * Hashtable hash function
128 */
129 static u_int hash(identification_t *id)
130 {
131 return chunk_hash_inc(id->get_encoding(id), id->get_type(id));
132 }
133
134 /**
135 * Hashtable equals function
136 */
137 static bool equals(identification_t *a, identification_t *b)
138 {
139 return a->equals(a, b);
140 }
141
142 /**
143 * Insert an address entry to a locked claimed/unclaimed hashtable
144 */
145 static void add_addr(private_eap_radius_provider_t *this,
146 hashtable_t *hashtable, identification_t *id, host_t *host)
147 {
148 entry_t *entry;
149
150 entry = get_or_create_entry(hashtable, id);
151 entry->addrs->insert_last(entry->addrs, host);
152 }
153
154 /**
155 * Remove the next address from the locked hashtable stored for given id
156 */
157 static host_t* remove_addr(private_eap_radius_provider_t *this,
158 hashtable_t *hashtable, identification_t *id)
159 {
160 entry_t *entry;
161 host_t *addr = NULL;
162
163 entry = hashtable->remove(hashtable, id);
164 if (entry)
165 {
166 entry->addrs->remove_first(entry->addrs, (void**)&addr);
167 put_or_destroy_entry(hashtable, entry);
168 }
169 return addr;
170 }
171
172 /**
173 * Clean up unclaimed leases assigned for an IKE_SA
174 */
175 static void release_unclaimed(private_listener_t *this, ike_sa_t *ike_sa)
176 {
177 identification_t *id;
178 entry_t *entry;
179
180 id = ike_sa->get_other_eap_id(ike_sa);
181 this->mutex->lock(this->mutex);
182 entry = this->unclaimed->remove(this->unclaimed, id);
183 this->mutex->unlock(this->mutex);
184 if (entry)
185 {
186 destroy_entry(entry);
187 }
188 }
189
190 METHOD(listener_t, message_hook, bool,
191 private_listener_t *this, ike_sa_t *ike_sa,
192 message_t *message, bool incoming, bool plain)
193 {
194 if (plain && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
195 !incoming && !message->get_request(message))
196 {
197 if ((ike_sa->get_version(ike_sa) == IKEV1 &&
198 message->get_exchange_type(message) == TRANSACTION) ||
199 (ike_sa->get_version(ike_sa) == IKEV2 &&
200 message->get_exchange_type(message) == IKE_AUTH))
201 {
202 /* if the addresses have not been claimed yet, they won't. Release
203 * these ressources. */
204 release_unclaimed(this, ike_sa);
205 }
206 }
207 return TRUE;
208 }
209
210 METHOD(listener_t, ike_updown, bool,
211 private_listener_t *this, ike_sa_t *ike_sa, bool up)
212 {
213 if (!up)
214 {
215 /* if the message hook does not apply because of a failed exchange
216 * or something, make sure we release any ressources now */
217 release_unclaimed(this, ike_sa);
218 }
219 return TRUE;
220 }
221
222 METHOD(attribute_provider_t, acquire_address, host_t*,
223 private_eap_radius_provider_t *this, linked_list_t *pools,
224 identification_t *id, host_t *requested)
225 {
226 enumerator_t *enumerator;
227 host_t *addr = NULL;
228 char *name;
229
230 enumerator = pools->create_enumerator(pools);
231 while (enumerator->enumerate(enumerator, &name))
232 {
233 if (streq(name, "radius"))
234 {
235 this->listener.mutex->lock(this->listener.mutex);
236 addr = remove_addr(this, this->listener.unclaimed, id);
237 if (addr)
238 {
239 add_addr(this, this->listener.claimed, id, addr->clone(addr));
240 }
241 this->listener.mutex->unlock(this->listener.mutex);
242 break;
243 }
244 }
245 enumerator->destroy(enumerator);
246
247 return addr;
248 }
249
250 METHOD(attribute_provider_t, release_address, bool,
251 private_eap_radius_provider_t *this, linked_list_t *pools, host_t *address,
252 identification_t *id)
253 {
254 enumerator_t *enumerator;
255 host_t *found = NULL;
256 char *name;
257
258 enumerator = pools->create_enumerator(pools);
259 while (enumerator->enumerate(enumerator, &name))
260 {
261 if (streq(name, "radius"))
262 {
263 this->listener.mutex->lock(this->listener.mutex);
264 found = remove_addr(this, this->listener.claimed, id);
265 this->listener.mutex->unlock(this->listener.mutex);
266 break;
267 }
268 }
269 enumerator->destroy(enumerator);
270
271 if (found)
272 {
273 found->destroy(found);
274 return TRUE;
275 }
276 return FALSE;
277 }
278
279 METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*,
280 private_eap_radius_provider_t *this, linked_list_t *pools,
281 identification_t *id, linked_list_t *vips)
282 {
283 return enumerator_create_empty();
284 }
285
286 METHOD(eap_radius_provider_t, add_framed_ip, void,
287 private_eap_radius_provider_t *this, identification_t *id, host_t *ip)
288 {
289 this->listener.mutex->lock(this->listener.mutex);
290 add_addr(this, this->listener.unclaimed, id, ip);
291 this->listener.mutex->unlock(this->listener.mutex);
292 }
293
294 METHOD(eap_radius_provider_t, destroy, void,
295 private_eap_radius_provider_t *this)
296 {
297 singleton = NULL;
298 charon->bus->remove_listener(charon->bus, &this->listener.public);
299 this->listener.mutex->destroy(this->listener.mutex);
300 this->listener.claimed->destroy(this->listener.claimed);
301 this->listener.unclaimed->destroy(this->listener.unclaimed);
302 free(this);
303 }
304
305 /**
306 * See header
307 */
308 eap_radius_provider_t *eap_radius_provider_create()
309 {
310 if (!singleton)
311 {
312 private_eap_radius_provider_t *this;
313
314 INIT(this,
315 .public = {
316 .provider = {
317 .acquire_address = _acquire_address,
318 .release_address = _release_address,
319 .create_attribute_enumerator = _create_attribute_enumerator,
320 },
321 .add_framed_ip = _add_framed_ip,
322 .destroy = _destroy,
323 },
324 .listener = {
325 .public = {
326 .ike_updown = _ike_updown,
327 .message = _message_hook,
328 },
329 .claimed = hashtable_create((hashtable_hash_t)hash,
330 (hashtable_equals_t)equals, 32),
331 .unclaimed = hashtable_create((hashtable_hash_t)hash,
332 (hashtable_equals_t)equals, 32),
333 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
334 },
335 );
336
337 charon->bus->add_listener(charon->bus, &this->listener.public);
338
339 singleton = &this->public;
340 }
341 return singleton;
342 }
343
344 /**
345 * See header
346 */
347 eap_radius_provider_t *eap_radius_provider_get()
348 {
349 return singleton;
350 }