payload: Use common prefixes for all payload type identifiers
[strongswan.git] / src / libcharon / plugins / eap_radius / eap_radius_forward.c
1 /*
2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 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_forward.h"
17
18 #include <daemon.h>
19 #include <collections/linked_list.h>
20 #include <collections/hashtable.h>
21 #include <threading/mutex.h>
22
23 typedef struct private_eap_radius_forward_t private_eap_radius_forward_t;
24
25 /**
26 * Private data of an eap_radius_forward_t object.
27 */
28 struct private_eap_radius_forward_t {
29
30 /**
31 * Public eap_radius_forward_t interface.
32 */
33 eap_radius_forward_t public;
34
35 /**
36 * List of attribute types to copy from IKE, as attr_t
37 */
38 linked_list_t *from_attr;
39
40 /**
41 * List of attribute types to copy to IKE, as attr_t
42 */
43 linked_list_t *to_attr;
44
45 /**
46 * Queued to forward from IKE, unique_id => linked_list_t of chunk_t
47 */
48 hashtable_t *from;
49
50 /**
51 * Queued to forward to IKE, unique_id => linked_list_t of chunk_t
52 */
53 hashtable_t *to;
54
55 /**
56 * Mutex to lock concurrent access to hashtables
57 */
58 mutex_t *mutex;
59 };
60
61 /**
62 * RADIUS attribute selector
63 */
64 typedef struct {
65 /** vendor ID, 0 for standard attributes */
66 u_int32_t vendor;
67 /** attribute type */
68 u_int8_t type;
69 } attr_t;
70
71 /**
72 * Single instance of this
73 */
74 static private_eap_radius_forward_t *singleton = NULL;
75
76 /**
77 * Free a queue entry
78 */
79 static void free_attribute(chunk_t *chunk)
80 {
81 free(chunk->ptr);
82 free(chunk);
83 }
84
85 /**
86 * Lookup/create an attribute queue from a table
87 */
88 static linked_list_t *lookup_queue(private_eap_radius_forward_t *this,
89 hashtable_t *table)
90 {
91 linked_list_t *queue = NULL;
92 ike_sa_t *ike_sa;
93 uintptr_t id;
94
95 ike_sa = charon->bus->get_sa(charon->bus);
96 if (ike_sa && ike_sa->supports_extension(ike_sa, EXT_STRONGSWAN))
97 {
98 id = ike_sa->get_unique_id(ike_sa);
99 this->mutex->lock(this->mutex);
100 queue = table->get(table, (void*)id);
101 if (!queue)
102 {
103 queue = linked_list_create();
104 table->put(table, (void*)id, queue);
105 }
106 this->mutex->unlock(this->mutex);
107 }
108 return queue;
109 }
110
111 /**
112 * Remove attribute queue from table
113 */
114 static void remove_queue(private_eap_radius_forward_t *this,
115 hashtable_t *table, ike_sa_t *ike_sa)
116 {
117 linked_list_t *queue;
118
119 this->mutex->lock(this->mutex);
120 queue = table->remove(table, (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
121 this->mutex->unlock(this->mutex);
122 if (queue)
123 {
124 queue->destroy_function(queue, (void*)free_attribute);
125 }
126 }
127
128 /**
129 * Check if RADIUS attribute is contained in selector
130 */
131 static bool is_attribute_selected(linked_list_t *selector,
132 radius_attribute_type_t type, chunk_t data)
133 {
134 enumerator_t *enumerator;
135 u_int32_t vendor = 0;
136 attr_t *sel;
137 bool found = FALSE;
138
139 if (type == RAT_VENDOR_SPECIFIC)
140 {
141 if (data.len < 4)
142 {
143 return FALSE;
144 }
145 vendor = untoh32(data.ptr);
146 }
147 enumerator = selector->create_enumerator(selector);
148 while (!found && enumerator->enumerate(enumerator, &sel))
149 {
150 if (sel->vendor == vendor)
151 {
152 if (vendor)
153 {
154 if (sel->type == 0)
155 { /* any of that vendor is fine */
156 found = TRUE;
157 }
158 else if (data.len > 4 && data.ptr[4] == sel->type)
159 { /* vendor specific type field, as defined in RFC 2865 */
160 found = TRUE;
161 }
162 }
163 else
164 {
165 if (sel->type == type)
166 {
167 found = TRUE;
168 }
169 }
170 }
171 }
172 enumerator->destroy(enumerator);
173
174 return found;
175 }
176
177 /**
178 * Copy RADIUS attributes from queue to a RADIUS message
179 */
180 static void queue2radius(linked_list_t *queue, radius_message_t *message)
181 {
182 chunk_t *data;
183
184 while (queue->remove_last(queue, (void**)&data) == SUCCESS)
185 {
186 if (data->len >= 2)
187 {
188 message->add(message, data->ptr[0], chunk_skip(*data, 2));
189 }
190 free_attribute(data);
191 }
192 }
193
194 /**
195 * Copy RADIUS attributes from a RADIUS message to the queue
196 */
197 static void radius2queue(radius_message_t *message, linked_list_t *queue,
198 linked_list_t *selector)
199 {
200 enumerator_t *enumerator;
201 int type;
202 chunk_t data, hdr, *ptr;
203
204 enumerator = message->create_enumerator(message);
205 while (enumerator->enumerate(enumerator, &type, &data))
206 {
207 if (is_attribute_selected(selector, type, data))
208 {
209 hdr = chunk_alloc(2);
210 hdr.ptr[0] = type;
211 hdr.ptr[1] = data.len + 2;
212
213 INIT(ptr);
214 *ptr = chunk_cat("mc", hdr, data);
215 queue->insert_last(queue, ptr);
216 }
217 }
218 enumerator->destroy(enumerator);
219 }
220
221 /**
222 * Copy RADIUS attribute nofifies from IKE message to queue
223 */
224 static void ike2queue(message_t *message, linked_list_t *queue,
225 linked_list_t *selector)
226 {
227 enumerator_t *enumerator;
228 payload_t *payload;
229 notify_payload_t *notify;
230 chunk_t data, *ptr;
231
232 enumerator = message->create_payload_enumerator(message);
233 while (enumerator->enumerate(enumerator, &payload))
234 {
235 if (payload->get_type(payload) == PLV2_NOTIFY ||
236 payload->get_type(payload) == PLV1_NOTIFY)
237 {
238 notify = (notify_payload_t*)payload;
239 if (notify->get_notify_type(notify) == RADIUS_ATTRIBUTE)
240 {
241 data = notify->get_notification_data(notify);
242 if (data.len >= 2 && is_attribute_selected(selector,
243 data.ptr[0], chunk_skip(data, 2)))
244 {
245 INIT(ptr);
246 *ptr = chunk_clone(data);
247 queue->insert_last(queue, ptr);
248 }
249 }
250 }
251 }
252 enumerator->destroy(enumerator);
253 }
254
255 /**
256 * Copy RADUIS attributes from queue to IKE message notifies
257 */
258 static void queue2ike(linked_list_t *queue, message_t *message)
259 {
260 chunk_t *data;
261
262 while (queue->remove_last(queue, (void**)&data) == SUCCESS)
263 {
264 message->add_notify(message, FALSE, RADIUS_ATTRIBUTE, *data);
265 free_attribute(data);
266 }
267 }
268
269 /**
270 * See header.
271 */
272 void eap_radius_forward_from_ike(radius_message_t *request)
273 {
274 private_eap_radius_forward_t *this = singleton;
275 linked_list_t *queue;
276
277 if (this)
278 {
279 queue = lookup_queue(this, this->from);
280 if (queue)
281 {
282 queue2radius(queue, request);
283 }
284 }
285 }
286
287 /**
288 * See header.
289 */
290 void eap_radius_forward_to_ike(radius_message_t *response)
291 {
292 private_eap_radius_forward_t *this = singleton;
293 linked_list_t *queue;
294
295 if (this)
296 {
297 queue = lookup_queue(this, this->to);
298 if (queue)
299 {
300 radius2queue(response, queue, this->to_attr);
301 }
302 }
303 }
304
305 METHOD(listener_t, message, bool,
306 private_eap_radius_forward_t *this,
307 ike_sa_t *ike_sa, message_t *message, bool incoming, bool plain)
308 {
309 linked_list_t *queue;
310
311 if (plain && message->get_exchange_type(message) == IKE_AUTH)
312 {
313 if (incoming)
314 {
315 queue = lookup_queue(this, this->from);
316 if (queue)
317 {
318 ike2queue(message, queue, this->from_attr);
319 }
320 }
321 else
322 {
323 queue = lookup_queue(this, this->to);
324 if (queue)
325 {
326 queue2ike(queue, message);
327 }
328 }
329 }
330 return TRUE;
331 }
332
333 METHOD(listener_t, ike_updown, bool,
334 private_eap_radius_forward_t *this, ike_sa_t *ike_sa, bool up)
335 {
336 /* up or down, we don't need the state anymore */
337 remove_queue(this, this->from, ike_sa);
338 remove_queue(this, this->to, ike_sa);
339 return TRUE;
340 }
341
342 /**
343 * Parse a selector string to a list of attr_t selectors
344 */
345 static linked_list_t* parse_selector(char *selector)
346 {
347 enumerator_t *enumerator;
348 linked_list_t *list;
349 char *token, *pos;
350
351 list = linked_list_create();
352 enumerator = enumerator_create_token(selector, ",", " ");
353 while (enumerator->enumerate(enumerator, &token))
354 {
355 int type, vendor = 0;
356 attr_t *attr;
357
358 pos = strchr(token, ':');
359 if (pos)
360 {
361 *(pos++) = 0;
362 vendor = atoi(token);
363 token = pos;
364 }
365 if (!enum_from_name(radius_attribute_type_names, token, &type))
366 {
367 type = atoi(token);
368 }
369 if (vendor == 0 && type == 0)
370 {
371 DBG1(DBG_CFG, "ignoring unknown RADIUS attribute type '%s'", token);
372 }
373 else
374 {
375 INIT(attr,
376 .type = type,
377 .vendor = vendor,
378 );
379 list->insert_last(list, attr);
380 if (!vendor)
381 {
382 DBG1(DBG_IKE, "forwarding RADIUS attribute %N",
383 radius_attribute_type_names, type);
384 }
385 else
386 {
387 DBG1(DBG_IKE, "forwarding RADIUS VSA %d-%d", vendor, type);
388 }
389 }
390 }
391 enumerator->destroy(enumerator);
392 return list;
393 }
394
395 METHOD(eap_radius_forward_t, destroy, void,
396 private_eap_radius_forward_t *this)
397 {
398 this->from_attr->destroy_function(this->from_attr, free);
399 this->to_attr->destroy_function(this->to_attr, free);
400 this->from->destroy(this->from);
401 this->to->destroy(this->to);
402 this->mutex->destroy(this->mutex);
403 free(this);
404 singleton = NULL;
405 }
406
407 /**
408 * See header
409 */
410 eap_radius_forward_t *eap_radius_forward_create()
411 {
412 private_eap_radius_forward_t *this;
413
414 INIT(this,
415 .public = {
416 .listener = {
417 .message = _message,
418 .ike_updown = _ike_updown,
419 },
420 .destroy = _destroy,
421 },
422 .from_attr = parse_selector(lib->settings->get_str(lib->settings,
423 "%s.plugins.eap-radius.forward.ike_to_radius", "",
424 lib->ns)),
425 .to_attr = parse_selector(lib->settings->get_str(lib->settings,
426 "%s.plugins.eap-radius.forward.radius_to_ike", "",
427 lib->ns)),
428 .from = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 8),
429 .to = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 8),
430 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
431 );
432
433 if (this->from_attr->get_count(this->from_attr) == 0 &&
434 this->to_attr->get_count(this->to_attr) == 0)
435 {
436 destroy(this);
437 return NULL;
438 }
439
440 singleton = this;
441 return &this->public;
442 }