Annotation of embedaddon/strongswan/src/libcharon/plugins/updown/updown_listener.c, revision 1.1.1.2

1.1       misho       1: /*
                      2:  * Copyright (C) 2013 Tobias Brunner
                      3:  * Copyright (C) 2008 Martin Willi
                      4:  * Copyright (C) 2016 Andreas Steffen
                      5:  * HSR Hochschule fuer Technik Rapperswil
                      6:  *
                      7:  * This program is free software; you can redistribute it and/or modify it
                      8:  * under the terms of the GNU General Public License as published by the
                      9:  * Free Software Foundation; either version 2 of the License, or (at your
                     10:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
                     11:  *
                     12:  * This program is distributed in the hope that it will be useful, but
                     13:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
                     14:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
                     15:  * for more details.
                     16:  */
                     17: 
                     18: #define _GNU_SOURCE
                     19: #include <stdio.h>
                     20: #include <unistd.h>
                     21: 
                     22: #include "updown_listener.h"
                     23: 
                     24: #include <utils/process.h>
                     25: #include <daemon.h>
                     26: #include <config/child_cfg.h>
                     27: 
                     28: typedef struct private_updown_listener_t private_updown_listener_t;
                     29: 
                     30: /**
                     31:  * Private data of an updown_listener_t object.
                     32:  */
                     33: struct private_updown_listener_t {
                     34: 
                     35:        /**
                     36:         * Public updown_listener_t interface.
                     37:         */
                     38:        updown_listener_t public;
                     39: 
                     40:        /**
                     41:         * List of cached interface names
                     42:         */
                     43:        linked_list_t *iface_cache;
                     44: 
                     45:        /**
                     46:         * DNS attribute handler
                     47:         */
                     48:        updown_handler_t *handler;
                     49: };
                     50: 
                     51: typedef struct cache_entry_t cache_entry_t;
                     52: 
                     53: /**
                     54:  * Cache line in the interface name cache.
                     55:  */
                     56: struct cache_entry_t {
1.1.1.2 ! misho      57:        /** reqid of the CHILD_SA */
1.1       misho      58:        uint32_t reqid;
                     59:        /** cached interface name */
                     60:        char *iface;
                     61: };
                     62: 
                     63: /**
                     64:  * Insert an interface name to the cache
                     65:  */
                     66: static void cache_iface(private_updown_listener_t *this, uint32_t reqid,
                     67:                                                char *iface)
                     68: {
                     69:        cache_entry_t *entry = malloc_thing(cache_entry_t);
                     70: 
                     71:        entry->reqid = reqid;
                     72:        entry->iface = strdup(iface);
                     73: 
                     74:        this->iface_cache->insert_first(this->iface_cache, entry);
                     75: }
                     76: 
                     77: /**
                     78:  * Remove a cached interface name and return it.
                     79:  */
                     80: static char* uncache_iface(private_updown_listener_t *this, uint32_t reqid)
                     81: {
                     82:        enumerator_t *enumerator;
                     83:        cache_entry_t *entry;
                     84:        char *iface = NULL;
                     85: 
                     86:        enumerator = this->iface_cache->create_enumerator(this->iface_cache);
                     87:        while (enumerator->enumerate(enumerator, &entry))
                     88:        {
                     89:                if (entry->reqid == reqid)
                     90:                {
                     91:                        this->iface_cache->remove_at(this->iface_cache, enumerator);
                     92:                        iface = entry->iface;
                     93:                        free(entry);
                     94:                        break;
                     95:                }
                     96:        }
                     97:        enumerator->destroy(enumerator);
                     98:        return iface;
                     99: }
                    100: 
                    101: /**
                    102:  * Allocate and push a format string to the environment
                    103:  */
                    104: static bool push_env(char *envp[], u_int count, char *fmt, ...)
                    105: {
                    106:        int i = 0;
                    107:        char *str;
                    108:        va_list args;
                    109: 
                    110:        while (envp[i])
                    111:        {
                    112:                if (++i + 1 >= count)
                    113:                {
                    114:                        return FALSE;
                    115:                }
                    116:        }
                    117:        va_start(args, fmt);
                    118:        if (vasprintf(&str, fmt, args) >= 0)
                    119:        {
                    120:                envp[i] = str;
                    121:        }
                    122:        va_end(args);
                    123:        return envp[i] != NULL;
                    124: }
                    125: 
                    126: /**
                    127:  * Free all allocated environment strings
                    128:  */
                    129: static void free_env(char *envp[])
                    130: {
                    131:        int i;
                    132: 
                    133:        for (i = 0; envp[i]; i++)
                    134:        {
                    135:                free(envp[i]);
                    136:        }
                    137: }
                    138: 
                    139: /**
                    140:  * Push variables for handled DNS attributes
                    141:  */
                    142: static void push_dns_env(private_updown_listener_t *this, ike_sa_t *ike_sa,
                    143:                                                 char *envp[], u_int count)
                    144: {
                    145:        enumerator_t *enumerator;
                    146:        host_t *host;
                    147:        int v4 = 0, v6 = 0;
                    148: 
                    149:        if (this->handler)
                    150:        {
                    151:                enumerator = this->handler->create_dns_enumerator(this->handler,
                    152:                                                                                        ike_sa->get_unique_id(ike_sa));
                    153:                while (enumerator->enumerate(enumerator, &host))
                    154:                {
                    155:                        switch (host->get_family(host))
                    156:                        {
                    157:                                case AF_INET:
                    158:                                        push_env(envp, count, "PLUTO_DNS4_%d=%H", ++v4, host);
                    159:                                        break;
                    160:                                case AF_INET6:
                    161:                                        push_env(envp, count, "PLUTO_DNS6_%d=%H", ++v6, host);
                    162:                                        break;
                    163:                                default:
                    164:                                        continue;
                    165:                        }
                    166:                }
                    167:                enumerator->destroy(enumerator);
                    168:        }
                    169: }
                    170: 
                    171: /**
                    172:  * Push variables for local/remote virtual IPs
                    173:  */
                    174: static void push_vip_env(private_updown_listener_t *this, ike_sa_t *ike_sa,
                    175:                                                 char *envp[], u_int count, bool local)
                    176: {
                    177:        enumerator_t *enumerator;
                    178:        host_t *host;
                    179:        int v4 = 0, v6 = 0;
                    180:        bool first = TRUE;
                    181: 
                    182:        enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local);
                    183:        while (enumerator->enumerate(enumerator, &host))
                    184:        {
                    185:                if (first)
                    186:                {       /* legacy variable for first VIP */
                    187:                        first = FALSE;
                    188:                        push_env(envp, count, "PLUTO_%s_SOURCEIP=%H",
                    189:                                         local ? "MY" : "PEER", host);
                    190:                }
                    191:                switch (host->get_family(host))
                    192:                {
                    193:                        case AF_INET:
                    194:                                push_env(envp, count, "PLUTO_%s_SOURCEIP4_%d=%H",
                    195:                                                 local ? "MY" : "PEER", ++v4, host);
                    196:                                break;
                    197:                        case AF_INET6:
                    198:                                push_env(envp, count, "PLUTO_%s_SOURCEIP6_%d=%H",
                    199:                                                 local ? "MY" : "PEER", ++v6, host);
                    200:                                break;
                    201:                        default:
                    202:                                continue;
                    203:                }
                    204:        }
                    205:        enumerator->destroy(enumerator);
                    206: }
                    207: 
                    208: #define        PORT_BUF_LEN    12
                    209: 
                    210: /**
                    211:  * Determine proper values for port env variable
                    212:  */
                    213: static char* get_port(traffic_selector_t *me, traffic_selector_t *other,
                    214:                                          char *port_buf, bool local)
                    215: {
                    216:        uint16_t port, to, from;
                    217: 
                    218:        switch (max(me->get_protocol(me), other->get_protocol(other)))
                    219:        {
                    220:                case IPPROTO_ICMP:
                    221:                case IPPROTO_ICMPV6:
                    222:                {
                    223:                        port = max(me->get_from_port(me), other->get_from_port(other));
                    224:                        snprintf(port_buf, PORT_BUF_LEN, "%u",
                    225:                                         local ? traffic_selector_icmp_type(port)
                    226:                                                   : traffic_selector_icmp_code(port));
                    227:                        return port_buf;
                    228:                }
                    229:        }
                    230:        if (local)
                    231:        {
                    232:                from = me->get_from_port(me);
                    233:                to   = me->get_to_port(me);
                    234:        }
                    235:        else
                    236:        {
                    237:                from = other->get_from_port(other);
                    238:                to   = other->get_to_port(other);
                    239:        }
                    240:        if (from == to || (from == 0 && to == 65535))
                    241:        {
                    242:                snprintf(port_buf, PORT_BUF_LEN, "%u", from);
                    243:        }
                    244:        else
                    245:        {
                    246:                snprintf(port_buf, PORT_BUF_LEN, "%u:%u", from, to);
                    247:        }
                    248:        return port_buf;
                    249: }
                    250: 
                    251: /**
                    252:  * Invoke the updown script once for given traffic selectors
                    253:  */
                    254: static void invoke_once(private_updown_listener_t *this, ike_sa_t *ike_sa,
                    255:                                                child_sa_t *child_sa, child_cfg_t *config, bool up,
                    256:                                                traffic_selector_t *my_ts, traffic_selector_t *other_ts)
                    257: {
                    258:        host_t *me, *other, *host;
                    259:        char *iface;
                    260:        uint8_t mask;
                    261:        uint32_t if_id;
                    262:        mark_t mark;
                    263:        bool is_host, is_ipv6;
                    264:        int out;
                    265:        FILE *shell;
                    266:        process_t *process;
                    267:        char port_buf[PORT_BUF_LEN];
                    268:        char *envp[128] = {};
                    269: 
                    270:        me = ike_sa->get_my_host(ike_sa);
                    271:        other = ike_sa->get_other_host(ike_sa);
                    272: 
                    273:        push_env(envp, countof(envp), "PATH=%s", getenv("PATH"));
                    274:        push_env(envp, countof(envp), "PLUTO_VERSION=1.1");
                    275:        is_host = my_ts->is_host(my_ts, me);
                    276:        if (is_host)
                    277:        {
                    278:                is_ipv6 = me->get_family(me) == AF_INET6;
                    279:        }
                    280:        else
                    281:        {
                    282:                is_ipv6 = my_ts->get_type(my_ts) == TS_IPV6_ADDR_RANGE;
                    283:        }
                    284:        push_env(envp, countof(envp), "PLUTO_VERB=%s%s%s",
                    285:                         up ? "up" : "down",
                    286:                         is_host ? "-host" : "-client",
                    287:                         is_ipv6 ? "-v6" : "");
                    288:        push_env(envp, countof(envp), "PLUTO_CONNECTION=%s",
                    289:                         config->get_name(config));
                    290:        if (up)
                    291:        {
                    292:                host = charon->kernel->get_nexthop(charon->kernel, other, -1, me,
                    293:                                                                                   &iface);
                    294:                if (host && iface)
                    295:                {
                    296:                        cache_iface(this, child_sa->get_reqid(child_sa), iface);
                    297:                }
                    298:                else
                    299:                {
                    300:                        iface = NULL;
                    301:                }
                    302:                DESTROY_IF(host);
                    303:        }
                    304:        else
                    305:        {
                    306:                iface = uncache_iface(this, child_sa->get_reqid(child_sa));
                    307:        }
                    308:        push_env(envp, countof(envp), "PLUTO_INTERFACE=%s",
                    309:                         iface ? iface : "unknown");
                    310:        push_env(envp, countof(envp), "PLUTO_REQID=%u",
                    311:                         child_sa->get_reqid(child_sa));
                    312:        push_env(envp, countof(envp), "PLUTO_PROTO=%s",
                    313:                         child_sa->get_protocol(child_sa) == PROTO_ESP ? "esp" : "ah");
                    314:        push_env(envp, countof(envp), "PLUTO_UNIQUEID=%u",
                    315:                         ike_sa->get_unique_id(ike_sa));
                    316:        push_env(envp, countof(envp), "PLUTO_ME=%H", me);
                    317:        push_env(envp, countof(envp), "PLUTO_MY_ID=%Y", ike_sa->get_my_id(ike_sa));
                    318:        if (!my_ts->to_subnet(my_ts, &host, &mask))
                    319:        {
                    320:                DBG1(DBG_CHD, "updown approximates local TS %R "
                    321:                                          "by next larger subnet", my_ts);
                    322:        }
                    323:        push_env(envp, countof(envp), "PLUTO_MY_CLIENT=%+H/%u", host, mask);
                    324:        host->destroy(host);
                    325:        push_env(envp, countof(envp), "PLUTO_MY_PORT=%s",
                    326:                         get_port(my_ts, other_ts, port_buf, TRUE));
                    327:        push_env(envp, countof(envp), "PLUTO_MY_PROTOCOL=%u",
                    328:                         my_ts->get_protocol(my_ts));
                    329:        push_env(envp, countof(envp), "PLUTO_PEER=%H", other);
                    330:        push_env(envp, countof(envp), "PLUTO_PEER_ID=%Y",
                    331:                         ike_sa->get_other_id(ike_sa));
                    332:        if (!other_ts->to_subnet(other_ts, &host, &mask))
                    333:        {
                    334:                DBG1(DBG_CHD, "updown approximates remote TS %R "
                    335:                                          "by next larger subnet", other_ts);
                    336:        }
                    337:        push_env(envp, countof(envp), "PLUTO_PEER_CLIENT=%+H/%u", host, mask);
                    338:        host->destroy(host);
                    339:        push_env(envp, countof(envp), "PLUTO_PEER_PORT=%s",
                    340:                         get_port(my_ts, other_ts, port_buf, FALSE));
                    341:        push_env(envp, countof(envp), "PLUTO_PEER_PROTOCOL=%u",
                    342:                         other_ts->get_protocol(other_ts));
                    343:        if (ike_sa->has_condition(ike_sa, COND_EAP_AUTHENTICATED) ||
                    344:                ike_sa->has_condition(ike_sa, COND_XAUTH_AUTHENTICATED))
                    345:        {
                    346:                push_env(envp, countof(envp), "PLUTO_XAUTH_ID=%Y",
                    347:                                 ike_sa->get_other_eap_id(ike_sa));
                    348:        }
                    349:        push_vip_env(this, ike_sa, envp, countof(envp), TRUE);
                    350:        push_vip_env(this, ike_sa, envp, countof(envp), FALSE);
                    351:        mark = child_sa->get_mark(child_sa, TRUE);
                    352:        if (mark.value)
                    353:        {
                    354:                push_env(envp, countof(envp), "PLUTO_MARK_IN=%u/0x%08x",
                    355:                                 mark.value, mark.mask);
                    356:        }
                    357:        mark = child_sa->get_mark(child_sa, FALSE);
                    358:        if (mark.value)
                    359:        {
                    360:                push_env(envp, countof(envp), "PLUTO_MARK_OUT=%u/0x%08x",
                    361:                                 mark.value, mark.mask);
                    362:        }
                    363:        if_id = child_sa->get_if_id(child_sa, TRUE);
                    364:        if (if_id)
                    365:        {
                    366:                push_env(envp, countof(envp), "PLUTO_IF_ID_IN=%u", if_id);
                    367:        }
                    368:        if_id = child_sa->get_if_id(child_sa, FALSE);
                    369:        if (if_id)
                    370:        {
                    371:                push_env(envp, countof(envp), "PLUTO_IF_ID_OUT=%u", if_id);
                    372:        }
                    373:        if (ike_sa->has_condition(ike_sa, COND_NAT_ANY))
                    374:        {
                    375:                push_env(envp, countof(envp), "PLUTO_UDP_ENC=%u",
                    376:                                 other->get_port(other));
                    377:        }
                    378:        if (child_sa->get_ipcomp(child_sa) != IPCOMP_NONE)
                    379:        {
                    380:                push_env(envp, countof(envp), "PLUTO_IPCOMP=1");
                    381:        }
                    382:        push_dns_env(this, ike_sa, envp, countof(envp));
                    383:        if (config->has_option(config, OPT_HOSTACCESS))
                    384:        {
                    385:                push_env(envp, countof(envp), "PLUTO_HOST_ACCESS=1");
                    386:        }
                    387: 
                    388:        process = process_start_shell(envp, NULL, &out, NULL, "2>&1 %s",
                    389:                                                                  config->get_updown(config));
                    390:        if (process)
                    391:        {
                    392:                shell = fdopen(out, "r");
                    393:                if (shell)
                    394:                {
                    395:                        while (TRUE)
                    396:                        {
                    397:                                char resp[128];
                    398: 
                    399:                                if (fgets(resp, sizeof(resp), shell) == NULL)
                    400:                                {
                    401:                                        if (ferror(shell))
                    402:                                        {
                    403:                                                DBG1(DBG_CHD, "error reading from updown script");
                    404:                                        }
                    405:                                        break;
                    406:                                }
                    407:                                else
                    408:                                {
                    409:                                        char *e = resp + strlen(resp);
                    410:                                        if (e > resp && e[-1] == '\n')
                    411:                                        {
                    412:                                                e[-1] = '\0';
                    413:                                        }
                    414:                                        DBG1(DBG_CHD, "updown: %s", resp);
                    415:                                }
                    416:                        }
                    417:                        fclose(shell);
                    418:                }
                    419:                else
                    420:                {
                    421:                        close(out);
                    422:                }
                    423:                process->wait(process, NULL);
                    424:        }
                    425:        free(iface);
                    426:        free_env(envp);
                    427: }
                    428: 
                    429: METHOD(listener_t, child_updown, bool,
                    430:        private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
                    431:        bool up)
                    432: {
                    433:        traffic_selector_t *my_ts, *other_ts;
                    434:        enumerator_t *enumerator;
                    435:        child_cfg_t *config;
                    436: 
                    437:        config = child_sa->get_config(child_sa);
                    438:        if (config->get_updown(config))
                    439:        {
                    440:                enumerator = child_sa->create_policy_enumerator(child_sa);
                    441:                while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
                    442:                {
                    443:                        invoke_once(this, ike_sa, child_sa, config, up, my_ts, other_ts);
                    444:                }
                    445:                enumerator->destroy(enumerator);
                    446:        }
                    447:        return TRUE;
                    448: }
                    449: 
                    450: METHOD(updown_listener_t, destroy, void,
                    451:        private_updown_listener_t *this)
                    452: {
                    453:        this->iface_cache->destroy(this->iface_cache);
                    454:        free(this);
                    455: }
                    456: 
                    457: /**
                    458:  * See header
                    459:  */
                    460: updown_listener_t *updown_listener_create(updown_handler_t *handler)
                    461: {
                    462:        private_updown_listener_t *this;
                    463: 
                    464:        INIT(this,
                    465:                .public = {
                    466:                        .listener = {
                    467:                                .child_updown = _child_updown,
                    468:                        },
                    469:                        .destroy = _destroy,
                    470:                },
                    471:                .iface_cache = linked_list_create(),
                    472:                .handler = handler,
                    473:        );
                    474: 
                    475:        return &this->public;
                    476: }

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