diff options
Diffstat (limited to 'src/pluto/kernel.c')
-rw-r--r-- | src/pluto/kernel.c | 2114 |
1 files changed, 0 insertions, 2114 deletions
diff --git a/src/pluto/kernel.c b/src/pluto/kernel.c deleted file mode 100644 index e4729ef08..000000000 --- a/src/pluto/kernel.c +++ /dev/null @@ -1,2114 +0,0 @@ -/* routines that interface with the kernel's IPsec mechanism - * - * Copyright (C) 2010 Tobias Brunner - * Copyright (C) 2009 Andreas Steffen - * Hochschule fuer Technik Rapperswil - * - * Copyright (C) 1998-2002 D. Hugh Redelmeier - * Copyright (C) 1997 Angelos D. Keromytis - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#include <stddef.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/queue.h> -#include <sys/wait.h> - -#include <sys/stat.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <freeswan.h> - -#include <library.h> -#include <hydra.h> -#include <crypto/rngs/rng.h> -#include <kernel/kernel_listener.h> - -#include <signal.h> -#include <sys/time.h> /* for select(2) */ -#include <sys/types.h> /* for select(2) */ -#include <pfkeyv2.h> -#include <pfkey.h> -#include "kameipsec.h" - -#include "constants.h" -#include "defs.h" -#include "connections.h" -#include "state.h" -#include "timer.h" -#include "kernel.h" -#include "kernel_pfkey.h" -#include "log.h" -#include "ca.h" -#include "server.h" -#include "whack.h" /* for RC_LOG_SERIOUS */ -#include "keys.h" -#include "crypto.h" -#include "nat_traversal.h" -#include "alg_info.h" -#include "kernel_alg.h" -#include "pluto.h" - - -bool can_do_IPcomp = TRUE; /* can system actually perform IPCOMP? */ - -/* test if the routes required for two different connections agree - * It is assumed that the destination subnets agree; we are only - * testing that the interfaces and nexthops match. - */ -#define routes_agree(c, d) ((c)->interface == (d)->interface \ - && sameaddr(&(c)->spd.this.host_nexthop, &(d)->spd.this.host_nexthop)) - -/* forward declaration */ -static bool shunt_eroute(connection_t *c, struct spd_route *sr, - enum routing_t rt_kind, unsigned int op, - const char *opname); - -static void set_text_said(char *text_said, const ip_address *dst, - ipsec_spi_t spi, int proto); - -/** - * Default IPsec SA config (e.g. to install trap policies). - */ -static ipsec_sa_cfg_t null_ipsec_sa = { - .mode = MODE_TRANSPORT, - .esp = { - .use = TRUE, - }, -}; - -/** - * Helper function that converts an ip_subnet to a traffic_selector_t. - */ -static traffic_selector_t *traffic_selector_from_subnet(const ip_subnet *client, - const u_int8_t proto) -{ - traffic_selector_t *ts; - host_t *net; - net = host_create_from_sockaddr((sockaddr_t*)&client->addr); - ts = traffic_selector_create_from_subnet(net, client->maskbits, proto, - net->get_port(net)); - return ts; -} - -/** - * Helper function that converts a traffic_selector_t to an ip_subnet. - */ -static ip_subnet subnet_from_traffic_selector(traffic_selector_t *ts) -{ - ip_subnet subnet; - host_t *net; - u_int8_t mask; - ts->to_subnet(ts, &net, &mask); - subnet.addr = *(ip_address*)net->get_sockaddr(net); - subnet.maskbits = mask; - net->destroy(net); - return subnet; -} - - -void record_and_initiate_opportunistic(const ip_subnet *ours, - const ip_subnet *his, - int transport_proto, const char *why) -{ - ip_address src, dst; - passert(samesubnettype(ours, his)); - - /* actually initiate opportunism */ - networkof(ours, &src); - networkof(his, &dst); - initiate_opportunistic(&src, &dst, transport_proto, TRUE, NULL_FD); -} - -/* Generate Unique SPI numbers. - * - * The returned SPI is in network byte order. - */ -ipsec_spi_t get_ipsec_spi(ipsec_spi_t avoid, int proto, struct spd_route *sr, - bool tunnel) -{ - host_t *host_src, *host_dst; - u_int32_t spi; - - host_src = host_create_from_sockaddr((sockaddr_t*)&sr->that.host_addr); - host_dst = host_create_from_sockaddr((sockaddr_t*)&sr->this.host_addr); - - if (hydra->kernel_interface->get_spi(hydra->kernel_interface, host_src, - host_dst, proto, sr->reqid, &spi) != SUCCESS) - { - spi = 0; - } - - host_src->destroy(host_src); - host_dst->destroy(host_dst); - - return spi; -} - -/* Generate Unique CPI numbers. - * The result is returned as an SPI (4 bytes) in network order! - * The real bits are in the nework-low-order 2 bytes. - */ -ipsec_spi_t get_my_cpi(struct spd_route *sr, bool tunnel) -{ - host_t *host_src, *host_dst; - u_int16_t cpi; - - host_src = host_create_from_sockaddr((sockaddr_t*)&sr->that.host_addr); - host_dst = host_create_from_sockaddr((sockaddr_t*)&sr->this.host_addr); - - if (hydra->kernel_interface->get_cpi(hydra->kernel_interface, host_src, - host_dst, sr->reqid, &cpi) != SUCCESS) - - { - cpi = 0; - } - - host_src->destroy(host_src); - host_dst->destroy(host_dst); - - return htonl((u_int32_t)ntohs(cpi)); -} - -/* Replace the shell metacharacters ', \, ", `, and $ in a character string - * by escape sequences consisting of their octal values - */ -static void escape_metachar(const char *src, char *dst, size_t dstlen) -{ - while (*src != '\0' && dstlen > 4) - { - switch (*src) - { - case '\'': - case '\\': - case '"': - case '`': - case '$': - sprintf(dst,"\\%s%o", (*src < 64)?"0":"", *src); - dst += 4; - dstlen -= 4; - break; - default: - *dst++ = *src; - dstlen--; - } - src++; - } - *dst = '\0'; -} - -/* invoke the updown script to do the routing and firewall commands required - * - * The user-specified updown script is run. Parameters are fed to it in - * the form of environment variables. All such environment variables - * have names starting with "PLUTO_". - * - * The operation to be performed is specified by PLUTO_VERB. This - * verb has a suffix "-host" if the client on this end is just the - * host; otherwise the suffix is "-client". If the address family - * of the host is IPv6, an extra suffix of "-v6" is added. - * - * "prepare-host" and "prepare-client" are used to delete a route - * that may exist (due to forces outside of Pluto). It is used to - * prepare for pluto creating a route. - * - * "route-host" and "route-client" are used to install a route. - * Since routing is based only on destination, the PLUTO_MY_CLIENT_* - * values are probably of no use (using them may signify a bug). - * - * "unroute-host" and "unroute-client" are used to delete a route. - * Since routing is based only on destination, the PLUTO_MY_CLIENT_* - * values are probably of no use (using them may signify a bug). - * - * "up-host" and "up-client" are run when an eroute is added (not replaced). - * They are useful for adjusting a firewall: usually for adding a rule - * to let processed packets flow between clients. Note that only - * one eroute may exist for a pair of client subnets but inbound - * IPsec SAs may persist without an eroute. - * - * "down-host" and "down-client" are run when an eroute is deleted. - * They are useful for adjusting a firewall. - */ - -#ifndef DEFAULT_UPDOWN -# define DEFAULT_UPDOWN "ipsec _updown" -#endif - -static bool do_command(connection_t *c, struct spd_route *sr, struct state *st, - const char *verb) -{ - char cmd[1536]; /* arbitrary limit on shell command length */ - const char *verb_suffix; - - /* figure out which verb suffix applies */ - { - const char *hs, *cs; - - switch (addrtypeof(&sr->this.host_addr)) - { - case AF_INET: - hs = "-host"; - cs = "-client"; - break; - case AF_INET6: - hs = "-host-v6"; - cs = "-client-v6"; - break; - default: - loglog(RC_LOG_SERIOUS, "unknown address family"); - return FALSE; - } - verb_suffix = subnetisaddr(&sr->this.client, &sr->this.host_addr) - ? hs : cs; - } - - /* form the command string */ - { - char - nexthop_str[sizeof("PLUTO_NEXT_HOP='' ") +ADDRTOT_BUF] = "", - srcip_str[sizeof("PLUTO_MY_SOURCEIP='' ")+ADDRTOT_BUF] = "", - me_str[ADDRTOT_BUF], - myid_str[BUF_LEN], - myclient_str[SUBNETTOT_BUF], - myclientnet_str[ADDRTOT_BUF], - myclientmask_str[ADDRTOT_BUF], - peer_str[ADDRTOT_BUF], - peerid_str[BUF_LEN], - peerclient_str[SUBNETTOT_BUF], - peerclientnet_str[ADDRTOT_BUF], - peerclientmask_str[ADDRTOT_BUF], - peerca_str[BUF_LEN], - mark_in[BUF_LEN] = "", - mark_out[BUF_LEN] = "", - udp_encap[BUF_LEN] = "", - xauth_id_str[BUF_LEN] = "", - secure_myid_str[BUF_LEN] = "", - secure_peerid_str[BUF_LEN] = "", - secure_peerca_str[BUF_LEN] = "", - secure_xauth_id_str[BUF_LEN] = ""; - ip_address ta; - pubkey_list_t *p; - - if (addrbytesptr(&sr->this.host_nexthop, NULL) - && !isanyaddr(&sr->this.host_nexthop)) - { - char *n; - - strcpy(nexthop_str, "PLUTO_NEXT_HOP='"); - n = nexthop_str + strlen(nexthop_str); - - addrtot(&sr->this.host_nexthop, 0 - ,n , sizeof(nexthop_str)-strlen(nexthop_str)); - strncat(nexthop_str, "' ", sizeof(nexthop_str)); - } - - if (!sr->this.host_srcip->is_anyaddr(sr->this.host_srcip)) - { - char *n; - - strcpy(srcip_str, "PLUTO_MY_SOURCEIP='"); - n = srcip_str + strlen(srcip_str); - snprintf(n, sizeof(srcip_str)-strlen(srcip_str), "%H", - sr->this.host_srcip); - strncat(srcip_str, "' ", sizeof(srcip_str)); - } - - if (sr->mark_in.value) - { - snprintf(mark_in, sizeof(mark_in), "PLUTO_MARK_IN='%u/0x%08x' ", - sr->mark_in.value, sr->mark_in.mask); - } - - if (sr->mark_out.value) - { - snprintf(mark_out, sizeof(mark_out), "PLUTO_MARK_OUT='%u/0x%08x' ", - sr->mark_out.value, sr->mark_out.mask); - } - - if (st && (st->nat_traversal & NAT_T_DETECTED)) - { - snprintf(udp_encap, sizeof(udp_encap), "PLUTO_UDP_ENC='%u' ", - sr->that.host_port); - } - - addrtot(&sr->this.host_addr, 0, me_str, sizeof(me_str)); - snprintf(myid_str, sizeof(myid_str), "%Y", sr->this.id); - escape_metachar(myid_str, secure_myid_str, sizeof(secure_myid_str)); - subnettot(&sr->this.client, 0, myclient_str, sizeof(myclientnet_str)); - networkof(&sr->this.client, &ta); - addrtot(&ta, 0, myclientnet_str, sizeof(myclientnet_str)); - maskof(&sr->this.client, &ta); - addrtot(&ta, 0, myclientmask_str, sizeof(myclientmask_str)); - - if (c->xauth_identity && - c->xauth_identity->get_type(c->xauth_identity) != ID_ANY) - { - snprintf(xauth_id_str, sizeof(xauth_id_str), "%Y", c->xauth_identity); - escape_metachar(xauth_id_str, secure_xauth_id_str, - sizeof(secure_xauth_id_str)); - snprintf(xauth_id_str, sizeof(xauth_id_str), "PLUTO_XAUTH_ID='%s' ", - secure_xauth_id_str); - } - - addrtot(&sr->that.host_addr, 0, peer_str, sizeof(peer_str)); - snprintf(peerid_str, sizeof(peerid_str), "%Y", sr->that.id); - escape_metachar(peerid_str, secure_peerid_str, sizeof(secure_peerid_str)); - subnettot(&sr->that.client, 0, peerclient_str, sizeof(peerclientnet_str)); - networkof(&sr->that.client, &ta); - addrtot(&ta, 0, peerclientnet_str, sizeof(peerclientnet_str)); - maskof(&sr->that.client, &ta); - addrtot(&ta, 0, peerclientmask_str, sizeof(peerclientmask_str)); - - for (p = pubkeys; p != NULL; p = p->next) - { - pubkey_t *key = p->key; - key_type_t type = key->public_key->get_type(key->public_key); - int pathlen; - - if (type == KEY_RSA && - sr->that.id->equals(sr->that.id, key->id) && - trusted_ca(key->issuer, sr->that.ca, &pathlen)) - { - if (key->issuer) - { - snprintf(peerca_str, BUF_LEN, "%Y", key->issuer); - escape_metachar(peerca_str, secure_peerca_str, BUF_LEN); - } - else - { - secure_peerca_str[0] = '\0'; - } - break; - } - } - - if (-1 == snprintf(cmd, sizeof(cmd) - , "2>&1 " /* capture stderr along with stdout */ - "PLUTO_VERSION='1.1' " /* change VERSION when interface spec changes */ - "PLUTO_VERB='%s%s' " - "PLUTO_CONNECTION='%s' " - "%s" /* optional PLUTO_NEXT_HOP */ - "PLUTO_INTERFACE='%s' " - "%s" /* optional PLUTO_HOST_ACCESS */ - "PLUTO_REQID='%u' " - "PLUTO_ME='%s' " - "PLUTO_MY_ID='%s' " - "PLUTO_MY_CLIENT='%s' " - "PLUTO_MY_CLIENT_NET='%s' " - "PLUTO_MY_CLIENT_MASK='%s' " - "PLUTO_MY_PORT='%u' " - "PLUTO_MY_PROTOCOL='%u' " - "PLUTO_PEER='%s' " - "PLUTO_PEER_ID='%s' " - "PLUTO_PEER_CLIENT='%s' " - "PLUTO_PEER_CLIENT_NET='%s' " - "PLUTO_PEER_CLIENT_MASK='%s' " - "PLUTO_PEER_PORT='%u' " - "PLUTO_PEER_PROTOCOL='%u' " - "PLUTO_PEER_CA='%s' " - "%s" /* optional PLUTO_MY_SRCIP */ - "%s" /* optional PLUTO_XAUTH_ID */ - "%s" /* optional PLUTO_MARK_IN */ - "%s" /* optional PLUTO_MARK_OUT */ - "%s" /* optional PLUTO_UDP_ENC */ - "%s" /* actual script */ - , verb, verb_suffix - , c->name - , nexthop_str - , c->interface->vname - , sr->this.hostaccess? "PLUTO_HOST_ACCESS='1' " : "" - , sr->reqid - , me_str - , secure_myid_str - , myclient_str - , myclientnet_str - , myclientmask_str - , sr->this.port - , sr->this.protocol - , peer_str - , secure_peerid_str - , peerclient_str - , peerclientnet_str - , peerclientmask_str - , sr->that.port - , sr->that.protocol - , secure_peerca_str - , srcip_str - , xauth_id_str - , mark_in - , mark_out - , udp_encap - , sr->this.updown == NULL? DEFAULT_UPDOWN : sr->this.updown)) - { - loglog(RC_LOG_SERIOUS, "%s%s command too long!", verb, verb_suffix); - return FALSE; - } - } - - DBG(DBG_CONTROL, DBG_log("executing %s%s: %s" - , verb, verb_suffix, cmd)); - - /* invoke the script, catching stderr and stdout - * It may be of concern that some file descriptors will - * be inherited. For the ones under our control, we - * have done fcntl(fd, F_SETFD, FD_CLOEXEC) to prevent this. - * Any used by library routines (perhaps the resolver or syslog) - * will remain. - */ - FILE *f = popen(cmd, "r"); - - if (f == NULL) - { - loglog(RC_LOG_SERIOUS, "unable to popen %s%s command", verb, verb_suffix); - return FALSE; - } - - /* log any output */ - for (;;) - { - /* if response doesn't fit in this buffer, it will be folded */ - char resp[256]; - - if (fgets(resp, sizeof(resp), f) == NULL) - { - if (ferror(f)) - { - log_errno((e, "fgets failed on output of %s%s command" - , verb, verb_suffix)); - return FALSE; - } - else - { - passert(feof(f)); - break; - } - } - else - { - char *e = resp + strlen(resp); - - if (e > resp && e[-1] == '\n') - e[-1] = '\0'; /* trim trailing '\n' */ - plog("%s%s output: %s", verb, verb_suffix, resp); - } - } - - /* report on and react to return code */ - { - int r = pclose(f); - - if (r == -1) - { - log_errno((e, "pclose failed for %s%s command" - , verb, verb_suffix)); - return FALSE; - } - else if (WIFEXITED(r)) - { - if (WEXITSTATUS(r) != 0) - { - loglog(RC_LOG_SERIOUS, "%s%s command exited with status %d" - , verb, verb_suffix, WEXITSTATUS(r)); - return FALSE; - } - } - else if (WIFSIGNALED(r)) - { - loglog(RC_LOG_SERIOUS, "%s%s command exited with signal %d" - , verb, verb_suffix, WTERMSIG(r)); - return FALSE; - } - else - { - loglog(RC_LOG_SERIOUS, "%s%s command exited with unknown status %d" - , verb, verb_suffix, r); - return FALSE; - } - } - return TRUE; -} - -/* Check that we can route (and eroute). Diagnose if we cannot. */ - -enum routability { - route_impossible = 0, - route_easy = 1, - route_nearconflict = 2, - route_farconflict = 3 -}; - -static enum routability could_route(connection_t *c) -{ - struct spd_route *esr, *rosr; - connection_t *ero /* who, if anyone, owns our eroute? */ - , *ro = route_owner(c, &rosr, &ero, &esr); /* who owns our route? */ - - /* it makes no sense to route a connection that is ISAKMP-only */ - if (!NEVER_NEGOTIATE(c->policy) && !HAS_IPSEC_POLICY(c->policy)) - { - loglog(RC_ROUTE, "cannot route an ISAKMP-only connection"); - return route_impossible; - } - - /* if this is a Road Warrior template, we cannot route. - * Opportunistic template is OK. - */ - if (c->kind == CK_TEMPLATE && !(c->policy & POLICY_OPPO)) - { - loglog(RC_ROUTE, "cannot route Road Warrior template"); - return route_impossible; - } - - /* if we don't know nexthop, we cannot route */ - if (isanyaddr(&c->spd.this.host_nexthop)) - { - loglog(RC_ROUTE, "cannot route connection without knowing our nexthop"); - return route_impossible; - } - - /* if routing would affect IKE messages, reject */ - if (c->spd.this.host_port != NAT_T_IKE_FLOAT_PORT - && c->spd.this.host_port != IKE_UDP_PORT - && addrinsubnet(&c->spd.that.host_addr, &c->spd.that.client)) - { - loglog(RC_LOG_SERIOUS, "cannot install route: peer is within its client"); - return route_impossible; - } - - /* If there is already a route for peer's client subnet - * and it disagrees about interface or nexthop, we cannot steal it. - * Note: if this connection is already routed (perhaps for another - * state object), the route will agree. - * This is as it should be -- it will arise during rekeying. - */ - if (ro != NULL && !routes_agree(ro, c)) - { - loglog(RC_LOG_SERIOUS, "cannot route -- route already in use for \"%s\"" - , ro->name); - return route_impossible; /* another connection already - using the eroute */ - } - - /* if there is an eroute for another connection, there is a problem */ - if (ero != NULL && ero != c) - { - connection_t *ero2, *ero_top; - connection_t *inside, *outside; - - /* - * note, wavesec (PERMANENT) goes *outside* and - * OE goes *inside* (TEMPLATE) - */ - inside = NULL; - outside= NULL; - if (ero->kind == CK_PERMANENT - && c->kind == CK_TEMPLATE) - { - outside = ero; - inside = c; - } - else if (c->kind == CK_PERMANENT - && ero->kind == CK_TEMPLATE) - { - outside = c; - inside = ero; - } - - /* okay, check again, with correct order */ - if (outside && outside->kind == CK_PERMANENT - && inside && inside->kind == CK_TEMPLATE) - { - char inst[CONN_INST_BUF]; - - /* this is a co-terminal attempt of the "near" kind. */ - /* when chaining, we chain from inside to outside */ - - /* XXX permit multiple deep connections? */ - passert(inside->policy_next == NULL); - - inside->policy_next = outside; - - /* since we are going to steal the eroute from the secondary - * policy, we need to make sure that it no longer thinks that - * it owns the eroute. - */ - outside->spd.eroute_owner = SOS_NOBODY; - outside->spd.routing = RT_UNROUTED_KEYED; - - /* set the priority of the new eroute owner to be higher - * than that of the current eroute owner - */ - inside->prio = outside->prio + 1; - - fmt_conn_instance(inside, inst); - - loglog(RC_LOG_SERIOUS - , "conflict on eroute (%s), switching eroute to %s and linking %s" - , inst, inside->name, outside->name); - - return route_nearconflict; - } - - /* look along the chain of policies for one with the same name */ - ero_top = ero; - - for (ero2 = ero; ero2 != NULL; ero2 = ero->policy_next) - { - if (ero2->kind == CK_TEMPLATE - && streq(ero2->name, c->name)) - break; - } - - /* If we fell of the end of the list, then we found no TEMPLATE - * so there must be a conflict that we can't resolve. - * As the names are not equal, then we aren't replacing/rekeying. - */ - if (ero2 == NULL) - { - char inst[CONN_INST_BUF]; - - fmt_conn_instance(ero, inst); - - loglog(RC_LOG_SERIOUS - , "cannot install eroute -- it is in use for \"%s\"%s #%lu" - , ero->name, inst, esr->eroute_owner); - return route_impossible; - } - } - return route_easy; -} - -bool trap_connection(connection_t *c) -{ - switch (could_route(c)) - { - case route_impossible: - return FALSE; - - case route_nearconflict: - case route_easy: - /* RT_ROUTED_TUNNEL is treated specially: we don't override - * because we don't want to lose track of the IPSEC_SAs etc. - */ - if (c->spd.routing < RT_ROUTED_TUNNEL) - { - return route_and_eroute(c, &c->spd, NULL); - } - return TRUE; - - case route_farconflict: - return FALSE; - } - - return FALSE; -} - -/** - * Delete any eroute for a connection and unroute it if route isn't shared - */ -void unroute_connection(connection_t *c) -{ - struct spd_route *sr; - enum routing_t cr; - - for (sr = &c->spd; sr; sr = sr->next) - { - cr = sr->routing; - - if (erouted(cr)) - { - /* cannot handle a live one */ - passert(sr->routing != RT_ROUTED_TUNNEL); - shunt_eroute(c, sr, RT_UNROUTED, ERO_DELETE, "delete"); - } - - sr->routing = RT_UNROUTED; /* do now so route_owner won't find us */ - - /* only unroute if no other connection shares it */ - if (routed(cr) && route_owner(c, NULL, NULL, NULL) == NULL) - { - (void) do_command(c, sr, NULL, "unroute"); - } - } -} - - -static void set_text_said(char *text_said, const ip_address *dst, - ipsec_spi_t spi, int proto) -{ - ip_said said; - - initsaid(dst, spi, proto, &said); - satot(&said, 0, text_said, SATOT_BUF); -} - - -/** - * Setup an IPsec route entry. - * op is one of the ERO_* operators. - */ -static bool raw_eroute(const ip_address *this_host, - const ip_subnet *this_client, - const ip_address *that_host, - const ip_subnet *that_client, - mark_t mark, - ipsec_spi_t spi, - unsigned int proto, - unsigned int satype, - unsigned int transport_proto, - ipsec_sa_cfg_t *sa, - unsigned int op, - const char *opname USED_BY_DEBUG) -{ - traffic_selector_t *ts_src, *ts_dst; - host_t *host_src, *host_dst; - policy_type_t type = POLICY_IPSEC; - policy_dir_t dir = POLICY_OUT; - policy_priority_t priority = POLICY_PRIORITY_DEFAULT; - char text_said[SATOT_BUF]; - bool ok = TRUE, - deleting = (op & ERO_MASK) == ERO_DELETE, - replacing = op & (SADB_X_SAFLAGS_REPLACEFLOW << ERO_FLAG_SHIFT); - - set_text_said(text_said, that_host, spi, proto); - - DBG(DBG_CONTROL | DBG_KERNEL, - { - int sport = ntohs(portof(&this_client->addr)); - int dport = ntohs(portof(&that_client->addr)); - char mybuf[SUBNETTOT_BUF]; - char peerbuf[SUBNETTOT_BUF]; - - subnettot(this_client, 0, mybuf, sizeof(mybuf)); - subnettot(that_client, 0, peerbuf, sizeof(peerbuf)); - DBG_log("%s eroute %s:%d -> %s:%d => %s:%d" - , opname, mybuf, sport, peerbuf, dport - , text_said, transport_proto); - }); - - if (satype == SADB_X_SATYPE_INT) - { - switch (ntohl(spi)) - { - case SPI_PASS: - type = POLICY_PASS; - break; - case SPI_DROP: - case SPI_REJECT: - type = POLICY_DROP; - break; - case SPI_TRAP: - case SPI_TRAPSUBNET: - case SPI_HOLD: - if (op & (SADB_X_SAFLAGS_INFLOW << ERO_FLAG_SHIFT)) - { - return TRUE; - } - priority = POLICY_PRIORITY_ROUTED; - break; - } - } - - if (op & (SADB_X_SAFLAGS_INFLOW << ERO_FLAG_SHIFT)) - { - dir = POLICY_IN; - } - - host_src = host_create_from_sockaddr((sockaddr_t*)this_host); - host_dst = host_create_from_sockaddr((sockaddr_t*)that_host); - ts_src = traffic_selector_from_subnet(this_client, transport_proto); - ts_dst = traffic_selector_from_subnet(that_client, transport_proto); - - if (deleting || replacing) - { - hydra->kernel_interface->del_policy(hydra->kernel_interface, - ts_src, ts_dst, dir, sa->reqid, mark, priority); - } - - if (!deleting) - { - ok = hydra->kernel_interface->add_policy(hydra->kernel_interface, - host_src, host_dst, ts_src, ts_dst, dir, type, sa, - mark, priority) == SUCCESS; - } - - if (dir == POLICY_IN) - { /* handle forward policy */ - dir = POLICY_FWD; - if (deleting || replacing) - { - hydra->kernel_interface->del_policy(hydra->kernel_interface, - ts_src, ts_dst, dir, sa->reqid, mark, priority); - } - - if (!deleting && ok && - (sa->mode == MODE_TUNNEL || satype == SADB_X_SATYPE_INT)) - { - ok = hydra->kernel_interface->add_policy(hydra->kernel_interface, - host_src, host_dst, ts_src, ts_dst, dir, type, sa, - mark, priority) == SUCCESS; - } - } - - host_src->destroy(host_src); - host_dst->destroy(host_dst); - ts_src->destroy(ts_src); - ts_dst->destroy(ts_dst); - - return ok; -} - -static bool eroute_connection(struct spd_route *sr, ipsec_spi_t spi, - unsigned int proto, unsigned int satype, - ipsec_sa_cfg_t *sa, unsigned int op, - const char *opname) -{ - const ip_address *peer = &sr->that.host_addr; - char buf2[256]; - bool ok; - - snprintf(buf2, sizeof(buf2) - , "eroute_connection %s", opname); - - if (proto == SA_INT) - { - peer = aftoinfo(addrtypeof(peer))->any; - } - ok = raw_eroute(peer, &sr->that.client, - &sr->this.host_addr, &sr->this.client, sr->mark_in, - spi, proto, satype, sr->this.protocol, - sa, op | (SADB_X_SAFLAGS_INFLOW << ERO_FLAG_SHIFT), buf2); - return raw_eroute(&sr->this.host_addr, &sr->this.client, peer, - &sr->that.client, sr->mark_out, spi, proto, satype, - sr->this.protocol, sa, op, buf2) && ok; -} - -/* assign a bare hold to a connection */ - -bool assign_hold(connection_t *c USED_BY_DEBUG, struct spd_route *sr, - int transport_proto, - const ip_address *src, - const ip_address *dst) -{ - /* either the automatically installed %hold eroute is broad enough - * or we try to add a broader one and delete the automatic one. - * Beware: this %hold might be already handled, but still squeak - * through because of a race. - */ - enum routing_t ro = sr->routing /* routing, old */ - , rn = ro; /* routing, new */ - - passert(LHAS(LELEM(CK_PERMANENT) | LELEM(CK_INSTANCE), c->kind)); - /* figure out what routing should become */ - switch (ro) - { - case RT_UNROUTED: - rn = RT_UNROUTED_HOLD; - break; - case RT_ROUTED_PROSPECTIVE: - rn = RT_ROUTED_HOLD; - break; - default: - /* no change: this %hold is old news and should just be deleted */ - break; - } - - /* We need a broad %hold - * First we ensure that there is a broad %hold. - * There may already be one (race condition): no need to create one. - * There may already be a %trap: replace it. - * There may not be any broad eroute: add %hold. - */ - if (rn != ro) - { - if (erouted(ro) - ? !eroute_connection(sr, htonl(SPI_HOLD), SA_INT, SADB_X_SATYPE_INT, - &null_ipsec_sa, ERO_REPLACE, - "replace %trap with broad %hold") - : !eroute_connection(sr, htonl(SPI_HOLD), SA_INT, SADB_X_SATYPE_INT, - &null_ipsec_sa, ERO_ADD, "add broad %hold")) - { - return FALSE; - } - } - sr->routing = rn; - return TRUE; -} - -/* install or remove eroute for SA Group */ -static bool sag_eroute(struct state *st, struct spd_route *sr, - unsigned op, const char *opname) -{ - u_int inner_proto, inner_satype; - ipsec_spi_t inner_spi = 0; - ipsec_sa_cfg_t sa = { - .mode = MODE_TRANSPORT, - }; - bool tunnel = FALSE; - - if (st->st_ah.present) - { - inner_spi = st->st_ah.attrs.spi; - inner_proto = SA_AH; - inner_satype = SADB_SATYPE_AH; - sa.ah.use = TRUE; - sa.ah.spi = inner_spi; - tunnel |= st->st_ah.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL; - } - - if (st->st_esp.present) - { - inner_spi = st->st_esp.attrs.spi; - inner_proto = SA_ESP; - inner_satype = SADB_SATYPE_ESP; - sa.esp.use = TRUE; - sa.esp.spi = inner_spi; - tunnel |= st->st_esp.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL; - } - - if (st->st_ipcomp.present) - { - inner_spi = st->st_ipcomp.attrs.spi; - inner_proto = SA_COMP; - inner_satype = SADB_X_SATYPE_COMP; - sa.ipcomp.transform = st->st_ipcomp.attrs.transid; - sa.ipcomp.cpi = htons(ntohl(inner_spi)); - tunnel |= st->st_ipcomp.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL; - } - - if (!sa.ah.use && !sa.esp.use && !sa.ipcomp.transform) - { - impossible(); /* no transform at all! */ - } - - if (tunnel) - { - inner_spi = st->st_tunnel_out_spi; - inner_proto = SA_IPIP; - inner_satype = SADB_X_SATYPE_IPIP; - sa.mode = MODE_TUNNEL; - } - - sa.reqid = sr->reqid; - - return eroute_connection(sr, inner_spi, inner_proto, inner_satype, - &sa, op, opname); -} - -/* compute a (host-order!) SPI to implement the policy in connection c */ -ipsec_spi_t -shunt_policy_spi(connection_t *c, bool prospective) -{ - /* note: these are in host order :-( */ - static const ipsec_spi_t shunt_spi[] = - { - SPI_TRAP, /* --initiateontraffic */ - SPI_PASS, /* --pass */ - SPI_DROP, /* --drop */ - SPI_REJECT, /* --reject */ - }; - - static const ipsec_spi_t fail_spi[] = - { - 0, /* --none*/ - SPI_PASS, /* --failpass */ - SPI_DROP, /* --faildrop */ - SPI_REJECT, /* --failreject */ - }; - - return prospective - ? shunt_spi[(c->policy & POLICY_SHUNT_MASK) >> POLICY_SHUNT_SHIFT] - : fail_spi[(c->policy & POLICY_FAIL_MASK) >> POLICY_FAIL_SHIFT]; -} - -/* Add/replace/delete a shunt eroute. - * Such an eroute determines the fate of packets without the use - * of any SAs. These are defaults, in effect. - * If a negotiation has not been attempted, use %trap. - * If negotiation has failed, the choice between %trap/%pass/%drop/%reject - * is specified in the policy of connection c. - */ -static bool shunt_eroute(connection_t *c, struct spd_route *sr, - enum routing_t rt_kind, - unsigned int op, const char *opname) -{ - /* We are constructing a special SAID for the eroute. - * The destination doesn't seem to matter, but the family does. - * The protocol is SA_INT -- mark this as shunt. - * The satype has no meaning, but is required for PF_KEY header! - * The SPI signifies the kind of shunt. - */ - ipsec_spi_t spi = shunt_policy_spi(c, rt_kind == RT_ROUTED_PROSPECTIVE); - - if (spi == 0) - { - /* we're supposed to end up with no eroute: rejig op and opname */ - switch (op) - { - case ERO_REPLACE: - /* replace with nothing == delete */ - op = ERO_DELETE; - opname = "delete"; - break; - case ERO_ADD: - /* add nothing == do nothing */ - return TRUE; - case ERO_DELETE: - /* delete remains delete */ - break; - default: - bad_case(op); - } - } - if (sr->routing == RT_ROUTED_ECLIPSED && c->kind == CK_TEMPLATE) - { - /* We think that we have an eroute, but we don't. - * Adjust the request and account for eclipses. - */ - passert(eclipsable(sr)); - switch (op) - { - case ERO_REPLACE: - /* really an add */ - op = ERO_ADD; - opname = "replace eclipsed"; - eclipse_count--; - break; - case ERO_DELETE: - /* delete unnecessary: we don't actually have an eroute */ - eclipse_count--; - return TRUE; - case ERO_ADD: - default: - bad_case(op); - } - } - else if (eclipse_count > 0 && op == ERO_DELETE && eclipsable(sr)) - { - /* maybe we are uneclipsing something */ - struct spd_route *esr; - connection_t *ue = eclipsed(c, &esr); - - if (ue != NULL) - { - esr->routing = RT_ROUTED_PROSPECTIVE; - return shunt_eroute(ue, esr - , RT_ROUTED_PROSPECTIVE, ERO_REPLACE, "restoring eclipsed"); - } - } - - return eroute_connection(sr, htonl(spi), SA_INT, SADB_X_SATYPE_INT, - &null_ipsec_sa, op, opname); -} - -static bool setup_half_ipsec_sa(struct state *st, bool inbound) -{ - host_t *host_src, *host_dst; - connection_t *c = st->st_connection; - struct end *src, *dst; - ipsec_mode_t mode = MODE_TRANSPORT; - ipsec_sa_cfg_t sa = { .mode = 0 }; - lifetime_cfg_t lt_none = { .time = { .rekey = 0 } }; - mark_t mark; - bool ok = TRUE; - /* SPIs, saved for undoing, if necessary */ - struct kernel_sa said[EM_MAXRELSPIS], *said_next = said; - if (inbound) - { - src = &c->spd.that; - dst = &c->spd.this; - mark = c->spd.mark_in; - } - else - { - src = &c->spd.this; - dst = &c->spd.that; - mark = c->spd.mark_out; - } - - host_src = host_create_from_sockaddr((sockaddr_t*)&src->host_addr); - host_dst = host_create_from_sockaddr((sockaddr_t*)&dst->host_addr); - - if (st->st_ah.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL - || st->st_esp.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL - || st->st_ipcomp.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL) - { - mode = MODE_TUNNEL; - } - - sa.mode = mode; - sa.reqid = c->spd.reqid; - - memset(said, 0, sizeof(said)); - - /* set up IPCOMP SA, if any */ - - if (st->st_ipcomp.present) - { - ipsec_spi_t ipcomp_spi = inbound ? st->st_ipcomp.our_spi - : st->st_ipcomp.attrs.spi; - - switch (st->st_ipcomp.attrs.transid) - { - case IPCOMP_DEFLATE: - break; - - default: - loglog(RC_LOG_SERIOUS, "IPCOMP transform %s not implemented", - enum_name(&ipcomp_transformid_names, - st->st_ipcomp.attrs.transid)); - goto fail; - } - - sa.ipcomp.cpi = htons(ntohl(ipcomp_spi)); - sa.ipcomp.transform = st->st_ipcomp.attrs.transid; - - said_next->spi = ipcomp_spi; - said_next->proto = IPPROTO_COMP; - - if (hydra->kernel_interface->add_sa(hydra->kernel_interface, host_src, - host_dst, ipcomp_spi, said_next->proto, c->spd.reqid, - mark, 0, <_none, ENCR_UNDEFINED, chunk_empty, - AUTH_UNDEFINED, chunk_empty, mode, - st->st_ipcomp.attrs.transid, 0 /* cpi */, FALSE, FALSE, - inbound, NULL, NULL) != SUCCESS) - { - goto fail; - } - said_next++; - mode = MODE_TRANSPORT; - } - - /* set up ESP SA, if any */ - - if (st->st_esp.present) - { - ipsec_spi_t esp_spi = inbound ? st->st_esp.our_spi - : st->st_esp.attrs.spi; - u_char *esp_dst_keymat = inbound ? st->st_esp.our_keymat - : st->st_esp.peer_keymat; - bool encap = st->nat_traversal & NAT_T_DETECTED; - encryption_algorithm_t enc_alg; - integrity_algorithm_t auth_alg; - const struct esp_info *ei; - chunk_t enc_key, auth_key; - u_int16_t key_len; - - if ((ei = kernel_alg_esp_info(st->st_esp.attrs.transid, - st->st_esp.attrs.auth)) == NULL) - { - loglog(RC_LOG_SERIOUS, "ESP transform %s / auth %s" - " not implemented yet", - enum_name(&esp_transform_names, st->st_esp.attrs.transid), - enum_name(&auth_alg_names, st->st_esp.attrs.auth)); - goto fail; - } - - key_len = st->st_esp.attrs.key_len / 8; - if (key_len) - { - /* XXX: must change to check valid _range_ key_len */ - if (key_len > ei->enckeylen) - { - loglog(RC_LOG_SERIOUS, "ESP transform %s: key_len=%d > %d", - enum_name(&esp_transform_names, st->st_esp.attrs.transid), - (int)key_len, (int)ei->enckeylen); - goto fail; - } - } - else - { - key_len = ei->enckeylen; - } - - switch (ei->transid) - { - case ESP_3DES: - /* 168 bits in kernel, need 192 bits for keymat_len */ - if (key_len == 21) - { - key_len = 24; - } - break; - case ESP_DES: - /* 56 bits in kernel, need 64 bits for keymat_len */ - if (key_len == 7) - { - key_len = 8; - } - break; - case ESP_AES_CCM_8: - case ESP_AES_CCM_12: - case ESP_AES_CCM_16: - key_len += 3; - break; - case ESP_AES_GCM_8: - case ESP_AES_GCM_12: - case ESP_AES_GCM_16: - case ESP_AES_CTR: - case ESP_AES_GMAC: - key_len += 4; - break; - default: - break; - } - - if (encap) - { - host_src->set_port(host_src, src->host_port); - host_dst->set_port(host_dst, dst->host_port); - // st->nat_oa is currently unused - } - - /* divide up keying material */ - enc_alg = encryption_algorithm_from_esp(st->st_esp.attrs.transid); - enc_key.ptr = esp_dst_keymat; - enc_key.len = key_len; - auth_alg = integrity_algorithm_from_esp(st->st_esp.attrs.auth); - auth_alg = auth_alg ? : AUTH_UNDEFINED; - auth_key.ptr = esp_dst_keymat + key_len; - auth_key.len = ei->authkeylen; - - sa.esp.use = TRUE; - sa.esp.spi = esp_spi; - - said_next->spi = esp_spi; - said_next->proto = IPPROTO_ESP; - - if (hydra->kernel_interface->add_sa(hydra->kernel_interface, host_src, - host_dst, esp_spi, said_next->proto, c->spd.reqid, - mark, 0, <_none, enc_alg, enc_key, - auth_alg, auth_key, mode, IPCOMP_NONE, 0 /* cpi */, - encap, FALSE, inbound, NULL, NULL) != SUCCESS) - { - goto fail; - } - said_next++; - mode = MODE_TRANSPORT; - } - - /* set up AH SA, if any */ - - if (st->st_ah.present) - { - ipsec_spi_t ah_spi = inbound ? st->st_ah.our_spi - : st->st_ah.attrs.spi; - u_char *ah_dst_keymat = inbound ? st->st_ah.our_keymat - : st->st_ah.peer_keymat; - integrity_algorithm_t auth_alg; - chunk_t auth_key; - - auth_alg = integrity_algorithm_from_esp(st->st_ah.attrs.auth); - auth_key.ptr = ah_dst_keymat; - auth_key.len = st->st_ah.keymat_len; - - sa.ah.use = TRUE; - sa.ah.spi = ah_spi; - - said_next->spi = ah_spi; - said_next->proto = IPPROTO_AH; - - if (hydra->kernel_interface->add_sa(hydra->kernel_interface, host_src, - host_dst, ah_spi, said_next->proto, c->spd.reqid, - mark, 0, <_none, ENCR_UNDEFINED, chunk_empty, - auth_alg, auth_key, mode, IPCOMP_NONE, 0 /* cpi */, - FALSE, FALSE, inbound, NULL, NULL) != SUCCESS) - { - goto fail; - } - said_next++; - mode = MODE_TRANSPORT; - } - - goto cleanup; - -fail: - /* undo the done SPIs */ - while (said_next-- != said) - { - hydra->kernel_interface->del_sa(hydra->kernel_interface, host_src, - host_dst, said_next->spi, - said_next->proto, 0 /* cpi */, - mark); - } - ok = FALSE; - -cleanup: - host_src->destroy(host_src); - host_dst->destroy(host_dst); - return ok; -} - -static bool teardown_half_ipsec_sa(struct state *st, bool inbound) -{ - connection_t *c = st->st_connection; - const struct end *src, *dst; - host_t *host_src, *host_dst; - ipsec_spi_t spi; - mark_t mark; - bool result = TRUE; - - if (inbound) - { - src = &c->spd.that; - dst = &c->spd.this; - mark = c->spd.mark_in; - } - else - { - src = &c->spd.this; - dst = &c->spd.that; - mark = c->spd.mark_out; - } - - host_src = host_create_from_sockaddr((sockaddr_t*)&src->host_addr); - host_dst = host_create_from_sockaddr((sockaddr_t*)&dst->host_addr); - - if (st->st_ah.present) - { - spi = inbound ? st->st_ah.our_spi : st->st_ah.attrs.spi; - result &= hydra->kernel_interface->del_sa(hydra->kernel_interface, - host_src, host_dst, spi, IPPROTO_AH, - 0 /* cpi */, mark) == SUCCESS; - } - - if (st->st_esp.present) - { - spi = inbound ? st->st_esp.our_spi : st->st_esp.attrs.spi; - result &= hydra->kernel_interface->del_sa(hydra->kernel_interface, - host_src, host_dst, spi, IPPROTO_ESP, - 0 /* cpi */, mark) == SUCCESS; - } - - if (st->st_ipcomp.present) - { - spi = inbound ? st->st_ipcomp.our_spi : st->st_ipcomp.attrs.spi; - result &= hydra->kernel_interface->del_sa(hydra->kernel_interface, - host_src, host_dst, spi, IPPROTO_COMP, - 0 /* cpi */, mark) == SUCCESS; - } - - host_src->destroy(host_src); - host_dst->destroy(host_dst); - - return result; -} - -/* - * get information about a given sa - */ -bool get_sa_info(struct state *st, bool inbound, u_int *bytes, time_t *use_time) -{ - connection_t *c = st->st_connection; - traffic_selector_t *ts_src = NULL, *ts_dst = NULL; - host_t *host_src = NULL, *host_dst = NULL; - const struct end *src, *dst; - ipsec_spi_t spi; - mark_t mark; - u_int64_t bytes_kernel = 0; - bool result = FALSE; - - *use_time = UNDEFINED_TIME; - - if (!st->st_esp.present) - { - goto failed; - } - - if (inbound) - { - src = &c->spd.that; - dst = &c->spd.this; - mark = c->spd.mark_in; - spi = st->st_esp.our_spi; - } - else - { - src = &c->spd.this; - dst = &c->spd.that; - mark = c->spd.mark_out; - spi = st->st_esp.attrs.spi; - } - - host_src = host_create_from_sockaddr((sockaddr_t*)&src->host_addr); - host_dst = host_create_from_sockaddr((sockaddr_t*)&dst->host_addr); - - switch(hydra->kernel_interface->query_sa(hydra->kernel_interface, host_src, - host_dst, spi, IPPROTO_ESP, - mark, &bytes_kernel)) - { - case FAILED: - goto failed; - case SUCCESS: - *bytes = bytes_kernel; - break; - case NOT_SUPPORTED: - default: - break; - } - - if (st->st_serialno == c->spd.eroute_owner) - { - u_int32_t time_kernel; - - ts_src = traffic_selector_from_subnet(&src->client, src->protocol); - ts_dst = traffic_selector_from_subnet(&dst->client, dst->protocol); - - if (hydra->kernel_interface->query_policy(hydra->kernel_interface, - ts_src, ts_dst, inbound ? POLICY_IN : POLICY_OUT, - mark, &time_kernel) != SUCCESS) - { - goto failed; - } - *use_time = time_kernel; - - if (inbound && - st->st_esp.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL) - { - if (hydra->kernel_interface->query_policy(hydra->kernel_interface, - ts_src, ts_dst, POLICY_FWD, mark, - &time_kernel) != SUCCESS) - { - goto failed; - } - *use_time = max(*use_time, time_kernel); - } - } - - result = TRUE; - -failed: - DESTROY_IF(host_src); - DESTROY_IF(host_dst); - DESTROY_IF(ts_src); - DESTROY_IF(ts_dst); - return result; -} - -/** - * Handler for kernel events (called by thread-pool thread) - */ -kernel_listener_t *kernel_handler; - -/** - * Data for acquire events - */ -typedef struct { - /** Subnets */ - ip_subnet src, dst; - /** Transport protocol */ - int proto; -} acquire_data_t; - -/** - * Callback for acquire events (called by main thread) - */ -void handle_acquire(acquire_data_t *this) -{ - record_and_initiate_opportunistic(&this->src, &this->dst, this->proto, - "%acquire"); -} - -METHOD(kernel_listener_t, acquire, bool, - kernel_listener_t *this, u_int32_t reqid, - traffic_selector_t *src_ts, traffic_selector_t *dst_ts) -{ - if (src_ts && dst_ts) - { - acquire_data_t *data; - DBG(DBG_CONTROL, - DBG_log("creating acquire event for policy %R === %R " - "with reqid {%u}", src_ts, dst_ts, reqid)); - INIT(data, - .src = subnet_from_traffic_selector(src_ts), - .dst = subnet_from_traffic_selector(dst_ts), - .proto = src_ts->get_protocol(src_ts), - ); - pluto->events->queue(pluto->events, (void*)handle_acquire, data, free); - } - else - { - DBG(DBG_CONTROL, - DBG_log("ignoring acquire without traffic selectors for policy " - "with reqid {%u}", reqid)); - } - DESTROY_IF(src_ts); - DESTROY_IF(dst_ts); - return TRUE; -} - -/** - * Data for mapping events - */ -typedef struct { - /** reqid, spi of affected SA */ - u_int32_t reqid, spi; - /** new endpont */ - ip_address new_end; -} mapping_data_t; - -/** - * Callback for mapping events (called by main thread) - */ -void handle_mapping(mapping_data_t *this) -{ - process_nat_t_new_mapping(this->reqid, this->spi, &this->new_end); -} - - -METHOD(kernel_listener_t, mapping, bool, - kernel_listener_t *this, u_int32_t reqid, u_int32_t spi, host_t *remote) -{ - mapping_data_t *data; - DBG(DBG_CONTROL, - DBG_log("creating mapping event for SA with SPI %.8x and reqid {%u}", - spi, reqid)); - INIT(data, - .reqid = reqid, - .spi = spi, - .new_end = *(ip_address*)remote->get_sockaddr(remote), - ); - pluto->events->queue(pluto->events, (void*)handle_mapping, data, free); - return TRUE; -} - -void init_kernel(void) -{ - /* register SA types that we can negotiate */ - can_do_IPcomp = FALSE; /* until we get a response from the kernel */ - pfkey_register(); - - INIT(kernel_handler, - .acquire = _acquire, - .mapping = _mapping, - ); - hydra->kernel_interface->add_listener(hydra->kernel_interface, - kernel_handler); -} - -void kernel_finalize() -{ - hydra->kernel_interface->remove_listener(hydra->kernel_interface, - kernel_handler); - free(kernel_handler); -} - -/* Note: install_inbound_ipsec_sa is only used by the Responder. - * The Responder will subsequently use install_ipsec_sa for the outbound. - * The Initiator uses install_ipsec_sa to install both at once. - */ -bool install_inbound_ipsec_sa(struct state *st) -{ - connection_t *const c = st->st_connection; - - /* If our peer has a fixed-address client, check if we already - * have a route for that client that conflicts. We will take this - * as proof that that route and the connections using it are - * obsolete and should be eliminated. Interestingly, this is - * the only case in which we can tell that a connection is obsolete. - */ - passert(c->kind == CK_PERMANENT || c->kind == CK_INSTANCE); - if (c->spd.that.has_client) - { - for (;;) - { - struct spd_route *esr; - connection_t *o = route_owner(c, &esr, NULL, NULL); - - if (o == NULL) - { - break; /* nobody has a route */ - } - - /* note: we ignore the client addresses at this end */ - if (sameaddr(&o->spd.that.host_addr, &c->spd.that.host_addr) && - o->interface == c->interface) - { - break; /* existing route is compatible */ - } - - if (o->kind == CK_TEMPLATE && streq(o->name, c->name)) - { - break; /* ??? is this good enough?? */ - } - - loglog(RC_LOG_SERIOUS, "route to peer's client conflicts with \"%s\" %s; releasing old connection to free the route" - , o->name, ip_str(&o->spd.that.host_addr)); - release_connection(o, FALSE); - } - } - - DBG(DBG_CONTROL, DBG_log("install_inbound_ipsec_sa() checking if we can route")); - /* check that we will be able to route and eroute */ - switch (could_route(c)) - { - case route_easy: - case route_nearconflict: - break; - default: - return FALSE; - } - - /* (attempt to) actually set up the SAs */ - return setup_half_ipsec_sa(st, TRUE); -} - -/* Install a route and then a prospective shunt eroute or an SA group eroute. - * Assumption: could_route gave a go-ahead. - * Any SA Group must have already been created. - * On failure, steps will be unwound. - */ -bool route_and_eroute(connection_t *c, struct spd_route *sr, struct state *st) -{ - struct spd_route *esr; - struct spd_route *rosr; - connection_t *ero /* who, if anyone, owns our eroute? */ - , *ro = route_owner(c, &rosr, &ero, &esr); - bool eroute_installed = FALSE - , firewall_notified = FALSE - , route_installed = FALSE; - - connection_t *ero_top; - - DBG(DBG_CONTROLMORE, - DBG_log("route_and_eroute with c: %s (next: %s) ero:%s esr:{%p} ro:%s rosr:{%p} and state: %lu" - , c->name - , (c->policy_next ? c->policy_next->name : "none") - , ero ? ero->name : "null" - , esr - , ro ? ro->name : "null" - , rosr - , st ? st->st_serialno : 0)); - - /* look along the chain of policies for one with the same name */ - ero_top = ero; - -#if 0 - /* XXX - mcr this made sense before, and likely will make sense - * again, so I'l leaving this to remind me what is up */ - if (ero!= NULL && ero->routing == RT_UNROUTED_KEYED) - ero = NULL; - - for (ero2 = ero; ero2 != NULL; ero2 = ero->policy_next) - if ((ero2->kind == CK_TEMPLATE || ero2->kind==CK_SECONDARY) - && streq(ero2->name, c->name)) - break; -#endif - - /* install the eroute */ - - if (ero != NULL) - { - /* We're replacing an eroute */ - - /* if no state provided, then install a shunt for later */ - if (st == NULL) - { - eroute_installed = shunt_eroute(c, sr, RT_ROUTED_PROSPECTIVE - , ERO_REPLACE, "replace"); - } - else - { - eroute_installed = sag_eroute(st, sr, ERO_REPLACE, "replace"); - } -#if 0 - /* XXX - MCR. I previously felt that this was a bogus check */ - if (ero != NULL && ero != c && esr != sr) - { - /* By elimination, we must be eclipsing ero. Check. */ - passert(ero->kind == CK_TEMPLATE && streq(ero->name, c->name)); - passert(LHAS(LELEM(RT_ROUTED_PROSPECTIVE) | LELEM(RT_ROUTED_ECLIPSED) - , esr->routing)); - passert(samesubnet(&esr->this.client, &sr->this.client) - && samesubnet(&esr->that.client, &sr->that.client)); - } -#endif - } - else - { - /* we're adding an eroute */ - - /* if no state provided, then install a shunt for later */ - if (st == NULL) - { - eroute_installed = shunt_eroute(c, sr, RT_ROUTED_PROSPECTIVE - , ERO_ADD, "add"); - } - else - { - eroute_installed = sag_eroute(st, sr, ERO_ADD, "add"); - } - } - - /* notify the firewall of a new tunnel */ - - if (eroute_installed) - { - /* do we have to notify the firewall? Yes, if we are installing - * a tunnel eroute and the firewall wasn't notified - * for a previous tunnel with the same clients. Any Previous - * tunnel would have to be for our connection, so the actual - * test is simple. - */ - firewall_notified = st == NULL /* not a tunnel eroute */ - || sr->eroute_owner != SOS_NOBODY /* already notified */ - || do_command(c, sr, st, "up"); /* go ahead and notify */ - } - - /* install the route */ - - DBG(DBG_CONTROL, - DBG_log("route_and_eroute: firewall_notified: %s" - , firewall_notified ? "true" : "false")); - if (!firewall_notified) - { - /* we're in trouble -- don't do routing */ - } - else if (ro == NULL) - { - /* a new route: no deletion required, but preparation is */ - (void) do_command(c, sr, st, "prepare"); /* just in case; ignore failure */ - route_installed = do_command(c, sr, st, "route"); - } - else if (routed(sr->routing) || routes_agree(ro, c)) - { - route_installed = TRUE; /* nothing to be done */ - } - else - { - /* Some other connection must own the route - * and the route must disagree. But since could_route - * must have allowed our stealing it, we'll do so. - * - * A feature of LINUX allows us to install the new route - * before deleting the old if the nexthops differ. - * This reduces the "window of vulnerability" when packets - * might flow in the clear. - */ - if (sameaddr(&sr->this.host_nexthop, &esr->this.host_nexthop)) - { - (void) do_command(ro, sr, st, "unroute"); - route_installed = do_command(c, sr, st, "route"); - } - else - { - route_installed = do_command(c, sr, st, "route"); - (void) do_command(ro, sr, st, "unroute"); - } - - /* record unrouting */ - if (route_installed) - { - do { - passert(!erouted(rosr->routing)); - rosr->routing = RT_UNROUTED; - - /* no need to keep old value */ - ro = route_owner(c, &rosr, NULL, NULL); - } while (ro != NULL); - } - } - - /* all done -- clean up */ - if (route_installed) - { - /* Success! */ - - if (ero != NULL && ero != c) - { - /* check if ero is an ancestor of c. */ - connection_t *ero2; - - for (ero2 = c; ero2 != NULL && ero2 != c; ero2 = ero2->policy_next) - ; - - if (ero2 == NULL) - { - /* By elimination, we must be eclipsing ero. Checked above. */ - if (ero->spd.routing != RT_ROUTED_ECLIPSED) - { - ero->spd.routing = RT_ROUTED_ECLIPSED; - eclipse_count++; - } - } - } - - if (st == NULL) - { - passert(sr->eroute_owner == SOS_NOBODY); - sr->routing = RT_ROUTED_PROSPECTIVE; - } - else - { - char cib[CONN_INST_BUF]; - sr->routing = RT_ROUTED_TUNNEL; - - DBG(DBG_CONTROL, - DBG_log("route_and_eroute: instance \"%s\"%s, setting eroute_owner {spd=%p,sr=%p} to #%ld (was #%ld) (newest_ipsec_sa=#%ld)" - , st->st_connection->name - , (fmt_conn_instance(st->st_connection, cib), cib) - , &st->st_connection->spd, sr - , st->st_serialno - , sr->eroute_owner - , st->st_connection->newest_ipsec_sa)); - sr->eroute_owner = st->st_serialno; - } - - return TRUE; - } - else - { - /* Failure! Unwind our work. */ - if (firewall_notified && sr->eroute_owner == SOS_NOBODY) - (void) do_command(c, sr, st, "down"); - - if (eroute_installed) - { - /* Restore original eroute, if we can. - * Since there is nothing much to be done if the restoration - * fails, ignore success or failure. - */ - if (ero != NULL) - { - /* restore ero's former glory */ - if (esr->eroute_owner == SOS_NOBODY) - { - /* note: normal or eclipse case */ - (void) shunt_eroute(ero, esr - , esr->routing, ERO_REPLACE, "restore"); - } - else - { - /* Try to find state that owned eroute. - * Don't do anything if it cannot be found. - * This case isn't likely since we don't run - * the updown script when replacing a SA group - * with its successor (for the same conn). - */ - struct state *ost = state_with_serialno(esr->eroute_owner); - - if (ost != NULL) - (void) sag_eroute(ost, esr, ERO_REPLACE, "restore"); - } - } - else - { - /* there was no previous eroute: delete whatever we installed */ - if (st == NULL) - { - (void) shunt_eroute(c, sr, sr->routing, ERO_DELETE, "delete"); - } - else - { - (void) sag_eroute(st, sr, ERO_DELETE, "delete"); - } - } - } - - return FALSE; - } -} - -bool install_ipsec_sa(struct state *st, bool inbound_also) -{ - struct spd_route *sr; - - DBG(DBG_CONTROL, DBG_log("install_ipsec_sa() for #%ld: %s" - , st->st_serialno - , inbound_also? - "inbound and outbound" : "outbound only")); - - switch (could_route(st->st_connection)) - { - case route_easy: - case route_nearconflict: - break; - default: - return FALSE; - } - - /* (attempt to) actually set up the SA group */ - if ((inbound_also && !setup_half_ipsec_sa(st, TRUE)) || - !setup_half_ipsec_sa(st, FALSE)) - { - return FALSE; - } - - for (sr = &st->st_connection->spd; sr != NULL; sr = sr->next) - { - DBG(DBG_CONTROL, DBG_log("sr for #%ld: %s" - , st->st_serialno - , enum_name(&routing_story, sr->routing))); - - /* - * if the eroute owner is not us, then make it us. - * See test co-terminal-02, pluto-rekey-01, pluto-unit-02/oppo-twice - */ - pexpect(sr->eroute_owner == SOS_NOBODY - || sr->routing >= RT_ROUTED_TUNNEL); - - if (sr->eroute_owner != st->st_serialno - && sr->routing != RT_UNROUTED_KEYED) - { - if (!route_and_eroute(st->st_connection, sr, st)) - { - delete_ipsec_sa(st, FALSE); - /* XXX go and unroute any SRs that were successfully - * routed already. - */ - return FALSE; - } - } - } - - return TRUE; -} - -/* delete an IPSEC SA. - * we may not succeed, but we bull ahead anyway because - * we cannot do anything better by recognizing failure - */ -void delete_ipsec_sa(struct state *st, bool inbound_only) -{ - if (!inbound_only) - { - /* If the state is the eroute owner, we must adjust - * the routing for the connection. - */ - connection_t *c = st->st_connection; - struct spd_route *sr; - - passert(st->st_connection); - - for (sr = &c->spd; sr; sr = sr->next) - { - if (sr->eroute_owner == st->st_serialno - && sr->routing == RT_ROUTED_TUNNEL) - { - sr->eroute_owner = SOS_NOBODY; - - /* Routing should become RT_ROUTED_FAILURE, - * but if POLICY_FAIL_NONE, then we just go - * right back to RT_ROUTED_PROSPECTIVE as if no - * failure happened. - */ - sr->routing = (c->policy & POLICY_FAIL_MASK) == POLICY_FAIL_NONE - ? RT_ROUTED_PROSPECTIVE : RT_ROUTED_FAILURE; - - (void) do_command(c, sr, st, "down"); - if ((c->policy & POLICY_DONT_REKEY) && c->kind == CK_INSTANCE) - { - /* in this special case, even if the connection - * is still alive (due to an ISAKMP SA), - * we get rid of routing. - * Even though there is still an eroute, the c->routing - * setting will convince unroute_connection to delete it. - * unroute_connection would be upset if c->routing == RT_ROUTED_TUNNEL - */ - unroute_connection(c); - } - else - { - (void) shunt_eroute(c, sr, sr->routing, ERO_REPLACE, "replace with shunt"); - } - } - } - (void) teardown_half_ipsec_sa(st, FALSE); - } - (void) teardown_half_ipsec_sa(st, TRUE); -} - -static bool update_nat_t_ipsec_esp_sa (struct state *st, bool inbound) -{ - connection_t *c = st->st_connection; - host_t *host_src, *host_dst, *new_src, *new_dst; - ipsec_spi_t spi = inbound ? st->st_esp.our_spi : st->st_esp.attrs.spi; - struct end *src = inbound ? &c->spd.that : &c->spd.this, - *dst = inbound ? &c->spd.this : &c->spd.that; - mark_t mark = inbound ? c->spd.mark_in : c->spd.mark_out; - bool result; - - host_src = host_create_from_sockaddr((sockaddr_t*)&src->host_addr); - host_dst = host_create_from_sockaddr((sockaddr_t*)&dst->host_addr); - - new_src = host_src->clone(host_src); - new_dst = host_dst->clone(host_dst); - new_src->set_port(new_src, src->host_port); - new_dst->set_port(new_dst, dst->host_port); - - result = hydra->kernel_interface->update_sa(hydra->kernel_interface, - spi, IPPROTO_ESP, 0 /* cpi */, host_src, host_dst, - new_src, new_dst, TRUE /* encap */, TRUE /* new_encap */, - mark) == SUCCESS; - - host_src->destroy(host_src); - host_dst->destroy(host_dst); - new_src->destroy(new_src); - new_dst->destroy(new_dst); - - return result; -} - -bool update_ipsec_sa (struct state *st) -{ - if (IS_IPSEC_SA_ESTABLISHED(st->st_state)) - { - if (st->st_esp.present && ( - (!update_nat_t_ipsec_esp_sa (st, TRUE)) || - (!update_nat_t_ipsec_esp_sa (st, FALSE)))) - { - return FALSE; - } - } - else if (IS_ONLY_INBOUND_IPSEC_SA_ESTABLISHED(st->st_state)) - { - if (st->st_esp.present && !update_nat_t_ipsec_esp_sa (st, FALSE)) - { - return FALSE; - } - } - else - { - DBG_log("assert failed at %s:%d st_state=%d", __FILE__, __LINE__, st->st_state); - return FALSE; - } - return TRUE; -} - -/* Check if there was traffic on given SA during the last idle_max - * seconds. If TRUE, the SA was idle and DPD exchange should be performed. - * If FALSE, DPD is not necessary. We also return TRUE for errors, as they - * could mean that the SA is broken and needs to be replace anyway. - */ -bool was_eroute_idle(struct state *st, time_t idle_max, time_t *idle_time) -{ - time_t use_time; - u_int bytes; - int ret = TRUE; - - passert(st != NULL); - - if (get_sa_info(st, TRUE, &bytes, &use_time) && use_time != UNDEFINED_TIME) - { - *idle_time = time_monotonic(NULL) - use_time; - ret = *idle_time >= idle_max; - } - - return ret; -} |