Annotation of embedaddon/strongswan/src/libcharon/plugins/eap_radius/eap_radius_forward.c, revision 1.1.1.1
1.1 misho 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: uint32_t vendor;
67: /** attribute type */
68: uint8_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: uint32_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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>