Annotation of embedaddon/strongswan/src/libcharon/plugins/attr_sql/attr_sql_provider.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (C) 2008 Martin Willi
        !             3:  * HSR Hochschule fuer Technik Rapperswil
        !             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 <time.h>
        !            17: 
        !            18: #include <utils/debug.h>
        !            19: #include <library.h>
        !            20: 
        !            21: #include "attr_sql_provider.h"
        !            22: 
        !            23: typedef struct private_attr_sql_provider_t private_attr_sql_provider_t;
        !            24: 
        !            25: /**
        !            26:  * private data of attr_sql_provider
        !            27:  */
        !            28: struct private_attr_sql_provider_t {
        !            29: 
        !            30:        /**
        !            31:         * public functions
        !            32:         */
        !            33:        attr_sql_provider_t public;
        !            34: 
        !            35:        /**
        !            36:         * database connection
        !            37:         */
        !            38:        database_t *db;
        !            39: 
        !            40:        /**
        !            41:         * whether to record lease history in lease table
        !            42:         */
        !            43:        bool history;
        !            44: };
        !            45: 
        !            46: /**
        !            47:  * lookup/insert an identity
        !            48:  */
        !            49: static u_int get_identity(private_attr_sql_provider_t *this, ike_sa_t *ike_sa)
        !            50: {
        !            51:        identification_t *id;
        !            52:        enumerator_t *e;
        !            53:        u_int row;
        !            54: 
        !            55:        id = ike_sa->get_other_eap_id(ike_sa);
        !            56: 
        !            57:        this->db->transaction(this->db, TRUE);
        !            58:        /* look for peer identity in the identities table */
        !            59:        e = this->db->query(this->db,
        !            60:                                                "SELECT id FROM identities WHERE type = ? AND data = ?",
        !            61:                                                DB_INT, id->get_type(id), DB_BLOB, id->get_encoding(id),
        !            62:                                                DB_UINT);
        !            63:        if (e && e->enumerate(e, &row))
        !            64:        {
        !            65:                e->destroy(e);
        !            66:                this->db->commit(this->db);
        !            67:                return row;
        !            68:        }
        !            69:        DESTROY_IF(e);
        !            70:        /* not found, insert new one */
        !            71:        if (this->db->execute(this->db, &row,
        !            72:                                  "INSERT INTO identities (type, data) VALUES (?, ?)",
        !            73:                                  DB_INT, id->get_type(id), DB_BLOB, id->get_encoding(id)) == 1)
        !            74:        {
        !            75:                this->db->commit(this->db);
        !            76:                return row;
        !            77:        }
        !            78:        this->db->rollback(this->db);
        !            79:        return 0;
        !            80: }
        !            81: 
        !            82: /**
        !            83:  * Lookup an attribute pool by name
        !            84:  */
        !            85: static u_int get_attr_pool(private_attr_sql_provider_t *this, char *name)
        !            86: {
        !            87:        enumerator_t *e;
        !            88:        u_int row = 0;
        !            89: 
        !            90:        e = this->db->query(this->db,
        !            91:                                                "SELECT id FROM attribute_pools WHERE name = ?",
        !            92:                                                DB_TEXT, name, DB_UINT);
        !            93:        if (e)
        !            94:        {
        !            95:                e->enumerate(e, &row);
        !            96:        }
        !            97:        DESTROY_IF(e);
        !            98: 
        !            99:        return row;
        !           100: }
        !           101: 
        !           102: /**
        !           103:  * Lookup pool by name and address family
        !           104:  */
        !           105: static u_int get_pool(private_attr_sql_provider_t *this, char *name, int family,
        !           106:                                          u_int *timeout)
        !           107: {
        !           108:        enumerator_t *e;
        !           109:        chunk_t start;
        !           110:        u_int pool;
        !           111: 
        !           112:        e = this->db->query(this->db,
        !           113:                                                "SELECT id, start, timeout FROM pools WHERE name = ?",
        !           114:                                                DB_TEXT, name, DB_UINT, DB_BLOB, DB_UINT);
        !           115:        if (e && e->enumerate(e, &pool, &start, timeout))
        !           116:        {
        !           117:                if ((family == AF_INET  && start.len == 4) ||
        !           118:                        (family == AF_INET6 && start.len == 16))
        !           119:                {
        !           120:                        e->destroy(e);
        !           121:                        return pool;
        !           122:                }
        !           123:        }
        !           124:        DESTROY_IF(e);
        !           125:        return 0;
        !           126: }
        !           127: 
        !           128: /**
        !           129:  * Look up an existing lease
        !           130:  */
        !           131: static host_t* check_lease(private_attr_sql_provider_t *this, char *name,
        !           132:                                                   u_int pool, u_int identity)
        !           133: {
        !           134:        while (TRUE)
        !           135:        {
        !           136:                u_int id;
        !           137:                chunk_t address;
        !           138:                enumerator_t *e;
        !           139:                time_t now = time(NULL);
        !           140: 
        !           141:                e = this->db->query(this->db,
        !           142:                                "SELECT id, address FROM addresses "
        !           143:                                "WHERE pool = ? AND identity = ? AND released != 0 LIMIT 1",
        !           144:                                DB_UINT, pool, DB_UINT, identity, DB_UINT, DB_BLOB);
        !           145:                if (!e || !e->enumerate(e, &id, &address))
        !           146:                {
        !           147:                        DESTROY_IF(e);
        !           148:                        break;
        !           149:                }
        !           150:                address = chunk_clonea(address);
        !           151:                e->destroy(e);
        !           152: 
        !           153:                if (this->db->execute(this->db, NULL,
        !           154:                                "UPDATE addresses SET acquired = ?, released = 0 "
        !           155:                                "WHERE id = ? AND identity = ? AND released != 0",
        !           156:                                DB_UINT, now, DB_UINT, id, DB_UINT, identity) > 0)
        !           157:                {
        !           158:                        host_t *host;
        !           159: 
        !           160:                        host = host_create_from_chunk(AF_UNSPEC, address, 0);
        !           161:                        if (host)
        !           162:                        {
        !           163:                                DBG1(DBG_CFG, "acquired existing lease for address %H in"
        !           164:                                         " pool '%s'", host, name);
        !           165:                                return host;
        !           166:                        }
        !           167:                }
        !           168:        }
        !           169:        return NULL;
        !           170: }
        !           171: 
        !           172: /**
        !           173:  * We check for unallocated addresses or expired leases. First we select an
        !           174:  * address as a candidate, but double check later on if it is still available
        !           175:  * during the update operation. This allows us to work without locking.
        !           176:  */
        !           177: static host_t* get_lease(private_attr_sql_provider_t *this, char *name,
        !           178:                                                 u_int pool, u_int timeout, u_int identity)
        !           179: {
        !           180:        while (TRUE)
        !           181:        {
        !           182:                u_int id;
        !           183:                chunk_t address;
        !           184:                enumerator_t *e;
        !           185:                time_t now = time(NULL);
        !           186:                int hits;
        !           187: 
        !           188:                if (timeout)
        !           189:                {
        !           190:                        /* check for an expired lease */
        !           191:                        e = this->db->query(this->db,
        !           192:                                "SELECT id, address FROM addresses "
        !           193:                                "WHERE pool = ? AND released != 0 AND released < ? LIMIT 1",
        !           194:                                DB_UINT, pool, DB_UINT, now - timeout, DB_UINT, DB_BLOB);
        !           195:                }
        !           196:                else
        !           197:                {
        !           198:                        /* with static leases, check for an unallocated address */
        !           199:                        e = this->db->query(this->db,
        !           200:                                "SELECT id, address FROM addresses "
        !           201:                                "WHERE pool = ? AND identity = 0 LIMIT 1",
        !           202:                                DB_UINT, pool, DB_UINT, DB_BLOB);
        !           203:                }
        !           204: 
        !           205:                if (!e || !e->enumerate(e, &id, &address))
        !           206:                {
        !           207:                        DESTROY_IF(e);
        !           208:                        break;
        !           209:                }
        !           210:                address = chunk_clonea(address);
        !           211:                e->destroy(e);
        !           212: 
        !           213:                if (timeout)
        !           214:                {
        !           215:                        hits = this->db->execute(this->db, NULL,
        !           216:                                                "UPDATE addresses SET "
        !           217:                                                "acquired = ?, released = 0, identity = ? "
        !           218:                                                "WHERE id = ? AND released != 0 AND released < ?",
        !           219:                                                DB_UINT, now, DB_UINT, identity,
        !           220:                                                DB_UINT, id, DB_UINT, now - timeout);
        !           221:                }
        !           222:                else
        !           223:                {
        !           224:                        hits = this->db->execute(this->db, NULL,
        !           225:                                                "UPDATE addresses SET "
        !           226:                                                "acquired = ?, released = 0, identity = ? "
        !           227:                                                "WHERE id = ? AND identity = 0",
        !           228:                                                DB_UINT, now, DB_UINT, identity, DB_UINT, id);
        !           229:                }
        !           230:                if (hits > 0)
        !           231:                {
        !           232:                        host_t *host;
        !           233: 
        !           234:                        host = host_create_from_chunk(AF_UNSPEC, address, 0);
        !           235:                        if (host)
        !           236:                        {
        !           237:                                DBG1(DBG_CFG, "acquired new lease for address %H in pool '%s'",
        !           238:                                         host, name);
        !           239:                                return host;
        !           240:                        }
        !           241:                }
        !           242:        }
        !           243:        DBG1(DBG_CFG, "no available address found in pool '%s'", name);
        !           244:        return NULL;
        !           245: }
        !           246: 
        !           247: METHOD(attribute_provider_t, acquire_address, host_t*,
        !           248:        private_attr_sql_provider_t *this, linked_list_t *pools, ike_sa_t *ike_sa,
        !           249:        host_t *requested)
        !           250: {
        !           251:        enumerator_t *enumerator;
        !           252:        host_t *address = NULL;
        !           253:        u_int identity, pool, timeout;
        !           254:        char *name;
        !           255:        int family;
        !           256: 
        !           257:        identity = get_identity(this, ike_sa);
        !           258:        if (identity)
        !           259:        {
        !           260:                family = requested->get_family(requested);
        !           261:                /* check for an existing lease in all pools */
        !           262:                enumerator = pools->create_enumerator(pools);
        !           263:                while (enumerator->enumerate(enumerator, &name))
        !           264:                {
        !           265:                        pool = get_pool(this, name, family, &timeout);
        !           266:                        if (pool)
        !           267:                        {
        !           268:                                address = check_lease(this, name, pool, identity);
        !           269:                                if (address)
        !           270:                                {
        !           271:                                        break;
        !           272:                                }
        !           273:                        }
        !           274:                }
        !           275:                enumerator->destroy(enumerator);
        !           276: 
        !           277:                if (!address)
        !           278:                {
        !           279:                        /* get an unallocated address or expired lease */
        !           280:                        enumerator = pools->create_enumerator(pools);
        !           281:                        while (enumerator->enumerate(enumerator, &name))
        !           282:                        {
        !           283:                                pool = get_pool(this, name, family, &timeout);
        !           284:                                if (pool)
        !           285:                                {
        !           286:                                        address = get_lease(this, name, pool, timeout, identity);
        !           287:                                        if (address)
        !           288:                                        {
        !           289:                                                break;
        !           290:                                        }
        !           291:                                }
        !           292:                        }
        !           293:                        enumerator->destroy(enumerator);
        !           294:                }
        !           295:        }
        !           296:        return address;
        !           297: }
        !           298: 
        !           299: METHOD(attribute_provider_t, release_address, bool,
        !           300:        private_attr_sql_provider_t *this, linked_list_t *pools, host_t *address,
        !           301:        ike_sa_t *ike_sa)
        !           302: {
        !           303:        enumerator_t *enumerator;
        !           304:        u_int pool, timeout;
        !           305:        time_t now = time(NULL);
        !           306:        bool found = FALSE;
        !           307:        char *name;
        !           308:        int family;
        !           309: 
        !           310:        family = address->get_family(address);
        !           311:        enumerator = pools->create_enumerator(pools);
        !           312:        while (enumerator->enumerate(enumerator, &name))
        !           313:        {
        !           314:                pool = get_pool(this, name, family, &timeout);
        !           315:                if (!pool)
        !           316:                {
        !           317:                        continue;
        !           318:                }
        !           319:                if (this->db->execute(this->db, NULL,
        !           320:                                "UPDATE addresses SET released = ? WHERE "
        !           321:                                "pool = ? AND address = ?", DB_UINT, time(NULL),
        !           322:                                DB_UINT, pool, DB_BLOB, address->get_address(address)) > 0)
        !           323:                {
        !           324:                        if (this->history)
        !           325:                        {
        !           326:                                this->db->execute(this->db, NULL,
        !           327:                                        "INSERT INTO leases (address, identity, acquired, released)"
        !           328:                                        " SELECT id, identity, acquired, ? FROM addresses "
        !           329:                                        " WHERE pool = ? AND address = ?",
        !           330:                                        DB_UINT, now, DB_UINT, pool,
        !           331:                                        DB_BLOB, address->get_address(address));
        !           332:                        }
        !           333:                        found = TRUE;
        !           334:                        break;
        !           335:                }
        !           336:        }
        !           337:        enumerator->destroy(enumerator);
        !           338: 
        !           339:        return found;
        !           340: }
        !           341: 
        !           342: METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*,
        !           343:        private_attr_sql_provider_t *this, linked_list_t *pools, ike_sa_t *ike_sa,
        !           344:        linked_list_t *vips)
        !           345: {
        !           346:        enumerator_t *attr_enumerator = NULL;
        !           347: 
        !           348:        if (vips->get_count(vips))
        !           349:        {
        !           350:                enumerator_t *pool_enumerator;
        !           351:                u_int count;
        !           352:                char *name;
        !           353: 
        !           354:                /* in a first step check for attributes that match name and id */
        !           355:                if (ike_sa)
        !           356:                {
        !           357:                        u_int identity = get_identity(this, ike_sa);
        !           358: 
        !           359:                        pool_enumerator = pools->create_enumerator(pools);
        !           360:                        while (pool_enumerator->enumerate(pool_enumerator, &name))
        !           361:                        {
        !           362:                                u_int attr_pool = get_attr_pool(this, name);
        !           363:                                if (!attr_pool)
        !           364:                                {
        !           365:                                        continue;
        !           366:                                }
        !           367: 
        !           368:                                attr_enumerator = this->db->query(this->db,
        !           369:                                                                "SELECT count(*) FROM attributes "
        !           370:                                                                "WHERE pool = ? AND identity = ?",
        !           371:                                                                DB_UINT, attr_pool, DB_UINT, identity, DB_UINT);
        !           372: 
        !           373:                                if (attr_enumerator &&
        !           374:                                        attr_enumerator->enumerate(attr_enumerator, &count) &&
        !           375:                                        count != 0)
        !           376:                                {
        !           377:                                        attr_enumerator->destroy(attr_enumerator);
        !           378:                                        attr_enumerator = this->db->query(this->db,
        !           379:                                                                "SELECT type, value FROM attributes "
        !           380:                                                                "WHERE pool = ? AND identity = ?", DB_UINT,
        !           381:                                                                attr_pool, DB_UINT, identity, DB_INT, DB_BLOB);
        !           382:                                        break;
        !           383:                                }
        !           384:                                DESTROY_IF(attr_enumerator);
        !           385:                                attr_enumerator = NULL;
        !           386:                        }
        !           387:                        pool_enumerator->destroy(pool_enumerator);
        !           388:                }
        !           389: 
        !           390:                /* in a second step check for attributes that match name */
        !           391:                if (!attr_enumerator)
        !           392:                {
        !           393:                        pool_enumerator = pools->create_enumerator(pools);
        !           394:                        while (pool_enumerator->enumerate(pool_enumerator, &name))
        !           395:                        {
        !           396:                                u_int attr_pool = get_attr_pool(this, name);
        !           397:                                if (!attr_pool)
        !           398:                                {
        !           399:                                        continue;
        !           400:                                }
        !           401: 
        !           402:                                attr_enumerator = this->db->query(this->db,
        !           403:                                                                        "SELECT count(*) FROM attributes "
        !           404:                                                                        "WHERE pool = ? AND identity = 0",
        !           405:                                                                        DB_UINT, attr_pool, DB_UINT);
        !           406: 
        !           407:                                if (attr_enumerator &&
        !           408:                                        attr_enumerator->enumerate(attr_enumerator, &count) &&
        !           409:                                        count != 0)
        !           410:                                {
        !           411:                                        attr_enumerator->destroy(attr_enumerator);
        !           412:                                        attr_enumerator = this->db->query(this->db,
        !           413:                                                                        "SELECT type, value FROM attributes "
        !           414:                                                                        "WHERE pool = ? AND identity = 0",
        !           415:                                                                        DB_UINT, attr_pool, DB_INT, DB_BLOB);
        !           416:                                        break;
        !           417:                                }
        !           418:                                DESTROY_IF(attr_enumerator);
        !           419:                                attr_enumerator = NULL;
        !           420:                        }
        !           421:                        pool_enumerator->destroy(pool_enumerator);
        !           422:                }
        !           423: 
        !           424:                /* lastly try to find global attributes */
        !           425:                if (!attr_enumerator)
        !           426:                {
        !           427:                        attr_enumerator = this->db->query(this->db,
        !           428:                                                                        "SELECT type, value FROM attributes "
        !           429:                                                                        "WHERE pool = 0 AND identity = 0",
        !           430:                                                                        DB_INT, DB_BLOB);
        !           431:                }
        !           432:        }
        !           433: 
        !           434:        return (attr_enumerator ? attr_enumerator : enumerator_create_empty());
        !           435: }
        !           436: 
        !           437: METHOD(attr_sql_provider_t, destroy, void,
        !           438:        private_attr_sql_provider_t *this)
        !           439: {
        !           440:        free(this);
        !           441: }
        !           442: 
        !           443: /*
        !           444:  * see header file
        !           445:  */
        !           446: attr_sql_provider_t *attr_sql_provider_create(database_t *db)
        !           447: {
        !           448:        private_attr_sql_provider_t *this;
        !           449: 
        !           450:        INIT(this,
        !           451:                .public = {
        !           452:                        .provider = {
        !           453:                                .acquire_address = _acquire_address,
        !           454:                                .release_address = _release_address,
        !           455:                                .create_attribute_enumerator = _create_attribute_enumerator,
        !           456:                        },
        !           457:                        .destroy = _destroy,
        !           458:                },
        !           459:                .db = db,
        !           460:                .history = lib->settings->get_bool(lib->settings,
        !           461:                                                "%s.plugins.attr-sql.lease_history", TRUE, lib->ns),
        !           462:        );
        !           463: 
        !           464:        if (lib->settings->get_bool(lib->settings,
        !           465:                                                "%s.plugins.attr-sql.crash_recovery", TRUE, lib->ns))
        !           466:        {
        !           467:                time_t now = time(NULL);
        !           468: 
        !           469:                /* close any "online" leases in the case we crashed */
        !           470:                if (this->history)
        !           471:                {
        !           472:                        this->db->execute(this->db, NULL,
        !           473:                                        "INSERT INTO leases (address, identity, acquired, released)"
        !           474:                                        " SELECT id, identity, acquired, ? FROM addresses "
        !           475:                                        " WHERE released = 0", DB_UINT, now);
        !           476:                }
        !           477:                this->db->execute(this->db, NULL,
        !           478:                                          "UPDATE addresses SET released = ? WHERE released = 0",
        !           479:                                          DB_UINT, now);
        !           480:        }
        !           481:        return &this->public;
        !           482: }

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