Annotation of embedaddon/strongswan/src/libcharon/plugins/eap_radius/eap_radius_provider.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * Copyright (C) 2018 Tobias Brunner
                      3:  * HSR Hochschule fuer Technik Rapperswil
                      4:  *
                      5:  * Copyright (C) 2013 Martin Willi
                      6:  * Copyright (C) 2013 revosec AG
                      7:  *
                      8:  * This program is free software; you can redistribute it and/or modify it
                      9:  * under the terms of the GNU General Public License as published by the
                     10:  * Free Software Foundation; either version 2 of the License, or (at your
                     11:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
                     12:  *
                     13:  * This program is distributed in the hope that it will be useful, but
                     14:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
                     15:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
                     16:  * for more details.
                     17:  */
                     18: 
                     19: #include "eap_radius_provider.h"
                     20: 
                     21: #include <daemon.h>
                     22: #include <collections/hashtable.h>
                     23: #include <threading/mutex.h>
                     24: 
                     25: typedef struct private_eap_radius_provider_t private_eap_radius_provider_t;
                     26: typedef struct private_listener_t private_listener_t;
                     27: 
                     28: /**
                     29:  * Private data of registered listener
                     30:  */
                     31: struct private_listener_t {
                     32: 
                     33:        /**
                     34:         * Implements listener_t interface
                     35:         */
                     36:        listener_t public;
                     37: 
                     38:        /**
                     39:         * Leases not acquired yet, identification_t => entry_t
                     40:         */
                     41:        hashtable_t *unclaimed;
                     42: 
                     43:        /**
                     44:         * Leases acquired, identification_t => entry_t
                     45:         */
                     46:        hashtable_t *claimed;
                     47: 
                     48:        /**
                     49:         * Mutex to lock leases
                     50:         */
                     51:        mutex_t *mutex;
                     52: };
                     53: 
                     54: /**
                     55:  * Private data of an eap_radius_provider_t object.
                     56:  */
                     57: struct private_eap_radius_provider_t {
                     58: 
                     59:        /**
                     60:         * Public eap_radius_provider_t interface.
                     61:         */
                     62:        eap_radius_provider_t public;
                     63: 
                     64:        /**
                     65:         * Additionally implements the listener_t interface
                     66:         */
                     67:        private_listener_t listener;
                     68: };
                     69: 
                     70: /**
                     71:  * Singleton instance of provider
                     72:  */
                     73: static eap_radius_provider_t *singleton = NULL;
                     74: 
                     75: /**
                     76:  * Configuration attribute in an entry
                     77:  */
                     78: typedef struct {
                     79:        /** type of attribute */
                     80:        configuration_attribute_type_t type;
                     81:        /** attribute data */
                     82:        chunk_t data;
                     83: } attr_t;
                     84: 
                     85: /**
                     86:  * Destroy an attr_t
                     87:  */
                     88: static void destroy_attr(attr_t *this)
                     89: {
                     90:        free(this->data.ptr);
                     91:        free(this);
                     92: }
                     93: 
                     94: /**
                     95:  * Hashtable entry with leases and attributes
                     96:  */
                     97: typedef struct {
                     98:        /** IKE_SA unique id we assign the IP lease */
                     99:        uintptr_t id;
                    100:        /** list of IP leases received from AAA, as host_t */
                    101:        linked_list_t *addrs;
                    102:        /** list of configuration attributes, as attr_t */
                    103:        linked_list_t *attrs;
                    104: } entry_t;
                    105: 
                    106: /**
                    107:  * destroy an entry_t
                    108:  */
                    109: static void destroy_entry(entry_t *this)
                    110: {
                    111:        this->addrs->destroy_offset(this->addrs, offsetof(host_t, destroy));
                    112:        this->attrs->destroy_function(this->attrs, (void*)destroy_attr);
                    113:        free(this);
                    114: }
                    115: 
                    116: /**
                    117:  * Get or create an entry from a locked hashtable
                    118:  */
                    119: static entry_t* get_or_create_entry(hashtable_t *hashtable, uintptr_t id)
                    120: {
                    121:        entry_t *entry;
                    122: 
                    123:        entry = hashtable->get(hashtable, (void*)id);
                    124:        if (!entry)
                    125:        {
                    126:                INIT(entry,
                    127:                        .id = id,
                    128:                        .addrs = linked_list_create(),
                    129:                        .attrs = linked_list_create(),
                    130:                );
                    131:                hashtable->put(hashtable, (void*)id, entry);
                    132:        }
                    133:        return entry;
                    134: }
                    135: 
                    136: /**
                    137:  * Put an entry to hashtable, or destroy it if empty
                    138:  */
                    139: static void put_or_destroy_entry(hashtable_t *hashtable, entry_t *entry)
                    140: {
                    141:        if (entry->addrs->get_count(entry->addrs) > 0 ||
                    142:                entry->attrs->get_count(entry->attrs) > 0)
                    143:        {
                    144:                hashtable->put(hashtable, (void*)entry->id, entry);
                    145:        }
                    146:        else
                    147:        {
                    148:                destroy_entry(entry);
                    149:        }
                    150: }
                    151: 
                    152: /**
                    153:  * Hashtable hash function
                    154:  */
                    155: static u_int hash(uintptr_t id)
                    156: {
                    157:        return id;
                    158: }
                    159: 
                    160: /**
                    161:  * Hashtable equals function
                    162:  */
                    163: static bool equals(uintptr_t a, uintptr_t b)
                    164: {
                    165:        return a == b;
                    166: }
                    167: 
                    168: /**
                    169:  * Insert an address entry to a locked claimed/unclaimed hashtable
                    170:  */
                    171: static void add_addr(private_eap_radius_provider_t *this,
                    172:                                         hashtable_t *hashtable, uintptr_t id, host_t *host)
                    173: {
                    174:        entry_t *entry;
                    175: 
                    176:        entry = get_or_create_entry(hashtable, id);
                    177:        entry->addrs->insert_last(entry->addrs, host);
                    178: }
                    179: 
                    180: /**
                    181:  * Remove the next address from the locked hashtable stored for given id
                    182:  */
                    183: static host_t* remove_addr(private_eap_radius_provider_t *this,
                    184:                                                   hashtable_t *hashtable, uintptr_t id, host_t *addr)
                    185: {
                    186:        enumerator_t *enumerator;
                    187:        entry_t *entry;
                    188:        host_t *found = NULL, *current;
                    189: 
                    190:        entry = hashtable->remove(hashtable, (void*)id);
                    191:        if (entry)
                    192:        {
                    193:                enumerator = entry->addrs->create_enumerator(entry->addrs);
                    194:                while (enumerator->enumerate(enumerator, &current))
                    195:                {
                    196:                        if (addr->ip_equals(addr, current))
                    197:                        {       /* prefer an exact match */
                    198:                                entry->addrs->remove_at(entry->addrs, enumerator);
                    199:                                enumerator->destroy(enumerator);
                    200:                                put_or_destroy_entry(hashtable, entry);
                    201:                                return current;
                    202:                        }
                    203:                        if (!found && addr->get_family(addr) == current->get_family(current))
                    204:                        {       /* fallback to the first IP with a matching address family */
                    205:                                found = current;
                    206:                        }
                    207:                }
                    208:                enumerator->destroy(enumerator);
                    209:                if (found)
                    210:                {
                    211:                        entry->addrs->remove(entry->addrs, found, NULL);
                    212:                }
                    213:                put_or_destroy_entry(hashtable, entry);
                    214:        }
                    215:        return found;
                    216: }
                    217: 
                    218: /**
                    219:  * Insert an attribute entry to a locked claimed/unclaimed hashtable
                    220:  */
                    221: static void add_attr(private_eap_radius_provider_t *this,
                    222:                                         hashtable_t *hashtable, uintptr_t id, attr_t *attr)
                    223: {
                    224:        entry_t *entry;
                    225: 
                    226:        entry = get_or_create_entry(hashtable, id);
                    227:        entry->attrs->insert_last(entry->attrs, attr);
                    228: }
                    229: 
                    230: /**
                    231:  * Remove the next attribute from the locked hashtable stored for given id
                    232:  */
                    233: static attr_t* remove_attr(private_eap_radius_provider_t *this,
                    234:                                                   hashtable_t *hashtable, uintptr_t id)
                    235: {
                    236:        entry_t *entry;
                    237:        attr_t *attr = NULL;
                    238: 
                    239:        entry = hashtable->remove(hashtable, (void*)id);
                    240:        if (entry)
                    241:        {
                    242:                entry->attrs->remove_first(entry->attrs, (void**)&attr);
                    243:                put_or_destroy_entry(hashtable, entry);
                    244:        }
                    245:        return attr;
                    246: }
                    247: 
                    248: /**
                    249:  * Clean up unclaimed leases assigned for an IKE_SA
                    250:  */
                    251: static void release_unclaimed(private_listener_t *this, ike_sa_t *ike_sa)
                    252: {
                    253:        uintptr_t id;
                    254:        entry_t *entry;
                    255: 
                    256:        id = ike_sa->get_unique_id(ike_sa);
                    257:        this->mutex->lock(this->mutex);
                    258:        entry = this->unclaimed->remove(this->unclaimed, (void*)id);
                    259:        this->mutex->unlock(this->mutex);
                    260:        if (entry)
                    261:        {
                    262:                destroy_entry(entry);
                    263:        }
                    264: }
                    265: 
                    266: METHOD(listener_t, message_hook, bool,
                    267:        private_listener_t *this, ike_sa_t *ike_sa,
                    268:        message_t *message, bool incoming, bool plain)
                    269: {
                    270:        if (plain && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
                    271:                !incoming && !message->get_request(message))
                    272:        {
                    273:                if ((ike_sa->get_version(ike_sa) == IKEV1 &&
                    274:                         message->get_exchange_type(message) == TRANSACTION) ||
                    275:                        (ike_sa->get_version(ike_sa) == IKEV2 &&
                    276:                         message->get_exchange_type(message) == IKE_AUTH))
                    277:                {
                    278:                        /* if the addresses have not been claimed yet, they won't. Release
                    279:                         * these resources. */
                    280:                        release_unclaimed(this, ike_sa);
                    281:                }
                    282:        }
                    283:        return TRUE;
                    284: }
                    285: 
                    286: METHOD(listener_t, ike_updown, bool,
                    287:        private_listener_t *this, ike_sa_t *ike_sa, bool up)
                    288: {
                    289:        if (!up)
                    290:        {
                    291:                /* if the message hook does not apply because of a failed exchange
                    292:                 * or something, make sure we release any resources now */
                    293:                release_unclaimed(this, ike_sa);
                    294:        }
                    295:        return TRUE;
                    296: }
                    297: 
                    298: /**
                    299:  * Migrate an entry in hashtable from old to new id
                    300:  */
                    301: static void migrate_entry(hashtable_t *table, uintptr_t old, uintptr_t new)
                    302: {
                    303:        entry_t *entry;
                    304: 
                    305:        entry = table->remove(table, (void*)old);
                    306:        if (entry)
                    307:        {
                    308:                entry->id = new;
                    309:                entry = table->put(table, (void*)new, entry);
                    310:                if (entry)
                    311:                {       /* shouldn't happen */
                    312:                        destroy_entry(entry);
                    313:                }
                    314:        }
                    315: }
                    316: 
                    317: METHOD(listener_t, ike_rekey, bool,
                    318:        private_listener_t *this, ike_sa_t *old, ike_sa_t *new)
                    319: {
                    320:        uintptr_t old_id, new_id;
                    321: 
                    322:        old_id = old->get_unique_id(old);
                    323:        new_id = new->get_unique_id(new);
                    324: 
                    325:        this->mutex->lock(this->mutex);
                    326: 
                    327:        migrate_entry(this->unclaimed, old_id, new_id);
                    328:        migrate_entry(this->claimed, old_id, new_id);
                    329: 
                    330:        this->mutex->unlock(this->mutex);
                    331: 
                    332:        return TRUE;
                    333: }
                    334: 
                    335: METHOD(attribute_provider_t, acquire_address, host_t*,
                    336:        private_eap_radius_provider_t *this, linked_list_t *pools,
                    337:        ike_sa_t *ike_sa, host_t *requested)
                    338: {
                    339:        enumerator_t *enumerator;
                    340:        host_t *addr = NULL;
                    341:        uintptr_t sa;
                    342:        char *name;
                    343: 
                    344:        sa = ike_sa->get_unique_id(ike_sa);
                    345: 
                    346:        enumerator = pools->create_enumerator(pools);
                    347:        while (enumerator->enumerate(enumerator, &name))
                    348:        {
                    349:                if (streq(name, "radius"))
                    350:                {
                    351:                        this->listener.mutex->lock(this->listener.mutex);
                    352:                        addr = remove_addr(this, this->listener.unclaimed, sa, requested);
                    353:                        if (addr)
                    354:                        {
                    355:                                add_addr(this, this->listener.claimed, sa, addr->clone(addr));
                    356:                        }
                    357:                        this->listener.mutex->unlock(this->listener.mutex);
                    358:                        break;
                    359:                }
                    360:        }
                    361:        enumerator->destroy(enumerator);
                    362: 
                    363:        return addr;
                    364: }
                    365: 
                    366: METHOD(attribute_provider_t, release_address, bool,
                    367:        private_eap_radius_provider_t *this, linked_list_t *pools, host_t *address,
                    368:        ike_sa_t *ike_sa)
                    369: {
                    370:        enumerator_t *enumerator;
                    371:        host_t *found = NULL;
                    372:        uintptr_t sa;
                    373:        char *name;
                    374: 
                    375:        sa = ike_sa->get_unique_id(ike_sa);
                    376: 
                    377:        enumerator = pools->create_enumerator(pools);
                    378:        while (enumerator->enumerate(enumerator, &name))
                    379:        {
                    380:                if (streq(name, "radius"))
                    381:                {
                    382:                        this->listener.mutex->lock(this->listener.mutex);
                    383:                        found = remove_addr(this, this->listener.claimed, sa, address);
                    384:                        this->listener.mutex->unlock(this->listener.mutex);
                    385:                        break;
                    386:                }
                    387:        }
                    388:        enumerator->destroy(enumerator);
                    389: 
                    390:        if (found)
                    391:        {
                    392:                found->destroy(found);
                    393:                return TRUE;
                    394:        }
                    395:        return FALSE;
                    396: }
                    397: 
                    398: /**
                    399:  * Enumerator implementation over attributes
                    400:  */
                    401: typedef struct {
                    402:        /** implements enumerator_t */
                    403:        enumerator_t public;
                    404:        /** list of attributes to enumerate */
                    405:        linked_list_t *list;
                    406:        /** currently enumerating attribute */
                    407:        attr_t *current;
                    408: } attribute_enumerator_t;
                    409: 
                    410: METHOD(enumerator_t, attribute_enumerate, bool,
                    411:        attribute_enumerator_t *this, va_list args)
                    412: {
                    413:        configuration_attribute_type_t *type;
                    414:        chunk_t *data;
                    415: 
                    416:        VA_ARGS_VGET(args, type, data);
                    417:        if (this->current)
                    418:        {
                    419:                destroy_attr(this->current);
                    420:                this->current = NULL;
                    421:        }
                    422:        if (this->list->remove_first(this->list, (void**)&this->current) == SUCCESS)
                    423:        {
                    424:                *type = this->current->type;
                    425:                *data = this->current->data;
                    426:                return TRUE;
                    427:        }
                    428:        return FALSE;
                    429: }
                    430: 
                    431: METHOD(enumerator_t, attribute_destroy, void,
                    432:        attribute_enumerator_t *this)
                    433: {
                    434:        if (this->current)
                    435:        {
                    436:                destroy_attr(this->current);
                    437:        }
                    438:        this->list->destroy_function(this->list, (void*)destroy_attr);
                    439:        free(this);
                    440: }
                    441: 
                    442: METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*,
                    443:        private_eap_radius_provider_t *this, linked_list_t *pools,
                    444:        ike_sa_t *ike_sa, linked_list_t *vips)
                    445: {
                    446:        attribute_enumerator_t *enumerator;
                    447:        attr_t *attr;
                    448:        uintptr_t sa;
                    449: 
                    450:        sa = ike_sa->get_unique_id(ike_sa);
                    451: 
                    452:        INIT(enumerator,
                    453:                .public = {
                    454:                        .enumerate = enumerator_enumerate_default,
                    455:                        .venumerate = _attribute_enumerate,
                    456:                        .destroy = _attribute_destroy,
                    457:                },
                    458:                .list = linked_list_create(),
                    459:        );
                    460: 
                    461:        /* we forward attributes regardless of pool configurations */
                    462:        this->listener.mutex->lock(this->listener.mutex);
                    463:        while (TRUE)
                    464:        {
                    465:                attr = remove_attr(this, this->listener.unclaimed, sa);
                    466:                if (!attr)
                    467:                {
                    468:                        break;
                    469:                }
                    470:                enumerator->list->insert_last(enumerator->list, attr);
                    471:        }
                    472:        this->listener.mutex->unlock(this->listener.mutex);
                    473: 
                    474:        return &enumerator->public;
                    475: }
                    476: 
                    477: METHOD(eap_radius_provider_t, add_framed_ip, void,
                    478:        private_eap_radius_provider_t *this, uint32_t id, host_t *ip)
                    479: {
                    480:        this->listener.mutex->lock(this->listener.mutex);
                    481:        add_addr(this, this->listener.unclaimed, id, ip);
                    482:        this->listener.mutex->unlock(this->listener.mutex);
                    483: }
                    484: 
                    485: METHOD(eap_radius_provider_t, add_attribute, void,
                    486:        private_eap_radius_provider_t *this, uint32_t id,
                    487:        configuration_attribute_type_t type, chunk_t data)
                    488: {
                    489:        attr_t *attr;
                    490: 
                    491:        INIT(attr,
                    492:                .type = type,
                    493:                .data = chunk_clone(data),
                    494:        );
                    495:        this->listener.mutex->lock(this->listener.mutex);
                    496:        add_attr(this, this->listener.unclaimed, id, attr);
                    497:        this->listener.mutex->unlock(this->listener.mutex);
                    498: }
                    499: 
                    500: METHOD(eap_radius_provider_t, clear_unclaimed, enumerator_t*,
                    501:        private_eap_radius_provider_t *this, uint32_t id)
                    502: {
                    503:        entry_t *entry;
                    504: 
                    505:        this->listener.mutex->lock(this->listener.mutex);
                    506:        entry = this->listener.unclaimed->remove(this->listener.unclaimed,
                    507:                                                                                         (void*)(uintptr_t)id);
                    508:        this->listener.mutex->unlock(this->listener.mutex);
                    509:        if (!entry)
                    510:        {
                    511:                return enumerator_create_empty();
                    512:        }
                    513:        return enumerator_create_cleaner(
                    514:                                        entry->addrs->create_enumerator(entry->addrs),
                    515:                                        (void*)destroy_entry, entry);
                    516: }
                    517: 
                    518: METHOD(eap_radius_provider_t, destroy, void,
                    519:        private_eap_radius_provider_t *this)
                    520: {
                    521:        singleton = NULL;
                    522:        charon->bus->remove_listener(charon->bus, &this->listener.public);
                    523:        this->listener.mutex->destroy(this->listener.mutex);
                    524:        this->listener.claimed->destroy(this->listener.claimed);
                    525:        this->listener.unclaimed->destroy(this->listener.unclaimed);
                    526:        free(this);
                    527: }
                    528: 
                    529: /**
                    530:  * See header
                    531:  */
                    532: eap_radius_provider_t *eap_radius_provider_create()
                    533: {
                    534:        if (!singleton)
                    535:        {
                    536:                private_eap_radius_provider_t *this;
                    537: 
                    538:                INIT(this,
                    539:                        .public = {
                    540:                                .provider = {
                    541:                                        .acquire_address = _acquire_address,
                    542:                                        .release_address = _release_address,
                    543:                                        .create_attribute_enumerator = _create_attribute_enumerator,
                    544:                                },
                    545:                                .add_framed_ip = _add_framed_ip,
                    546:                                .add_attribute = _add_attribute,
                    547:                                .clear_unclaimed = _clear_unclaimed,
                    548:                                .destroy = _destroy,
                    549:                        },
                    550:                        .listener = {
                    551:                                .public = {
                    552:                                        .ike_updown = _ike_updown,
                    553:                                        .ike_rekey = _ike_rekey,
                    554:                                        .message = _message_hook,
                    555:                                },
                    556:                                .claimed = hashtable_create((hashtable_hash_t)hash,
                    557:                                                                                (hashtable_equals_t)equals, 32),
                    558:                                .unclaimed = hashtable_create((hashtable_hash_t)hash,
                    559:                                                                                (hashtable_equals_t)equals, 32),
                    560:                                .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
                    561:                        },
                    562:                );
                    563: 
                    564:                if (lib->settings->get_bool(lib->settings,
                    565:                                                        "%s.plugins.eap-radius.accounting", FALSE, lib->ns))
                    566:                {
                    567:                        /* if RADIUS accounting is enabled, keep unclaimed IPs around until
                    568:                         * the Accounting-Stop message is sent */
                    569:                        this->listener.public.message = NULL;
                    570:                }
                    571: 
                    572:                charon->bus->add_listener(charon->bus, &this->listener.public);
                    573: 
                    574:                singleton = &this->public;
                    575:        }
                    576:        return singleton;
                    577: }
                    578: 
                    579: /**
                    580:  * See header
                    581:  */
                    582: eap_radius_provider_t *eap_radius_provider_get()
                    583: {
                    584:        return singleton;
                    585: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>