diff options
Diffstat (limited to 'programs/pluto/kernel.c')
-rw-r--r-- | programs/pluto/kernel.c | 2999 |
1 files changed, 0 insertions, 2999 deletions
diff --git a/programs/pluto/kernel.c b/programs/pluto/kernel.c deleted file mode 100644 index d2070c0d4..000000000 --- a/programs/pluto/kernel.c +++ /dev/null @@ -1,2999 +0,0 @@ -/* routines that interface with the kernel's IPsec mechanism - * Copyright (C) 1997 Angelos D. Keromytis. - * Copyright (C) 1998-2002 D. Hugh Redelmeier. - * - * 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. - * - * RCSID $Id: kernel.c,v 1.26 2006/04/29 18:16:02 as Exp $ - */ - -#include <stddef.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <wait.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/queue.h> - -#include <sys/stat.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <freeswan.h> -#include <freeswan/ipsec_policy.h> - -#ifdef KLIPS -#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" -#endif /* KLIPS */ - -#include "constants.h" -#include "defs.h" -#include "rnd.h" -#include "id.h" -#include "connections.h" -#include "state.h" -#include "timer.h" -#include "kernel.h" -#include "kernel_netlink.h" -#include "kernel_pfkey.h" -#include "kernel_noklips.h" -#include "log.h" -#include "ca.h" -#include "server.h" -#include "whack.h" /* for RC_LOG_SERIOUS */ -#include "keys.h" - -#ifdef NAT_TRAVERSAL -#include "packet.h" /* for pb_stream in nat_traversal.h */ -#include "nat_traversal.h" -#endif - -#include "alg_info.h" -#include "kernel_alg.h" - - -bool can_do_IPcomp = TRUE; /* can system actually perform IPCOMP? */ - -/* How far can IPsec messages arrive out of order before the anti-replay - * logic loses track and swats them? 64 is the best KLIPS can do. - * And 32 is the best XFRM can do... - */ -#define REPLAY_WINDOW 64 -#define REPLAY_WINDOW_XFRM 32 - -/* 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)) - -#ifndef KLIPS - -bool no_klips = TRUE; /* don't actually use KLIPS */ - -#else /* !KLIPS */ - -/* bare (connectionless) shunt (eroute) table - * - * Bare shunts are those that don't "belong" to a connection. - * This happens because some %trapped traffic hasn't yet or cannot be - * assigned to a connection. The usual reason is that we cannot discover - * the peer SG. Another is that even when the peer has been discovered, - * it may be that no connection matches all the particulars. - * We record them so that, with scanning, we can discover - * which %holds are news and which others should expire. - */ - -#define SHUNT_SCAN_INTERVAL (60 * 2) /* time between scans of eroutes */ - -/* SHUNT_PATIENCE only has resolution down to a multiple of the sample rate, - * SHUNT_SCAN_INTERVAL. - * By making SHUNT_PATIENCE an odd multiple of half of SHUNT_SCAN_INTERVAL, - * we minimize the effects of jitter. - */ -#define SHUNT_PATIENCE (SHUNT_SCAN_INTERVAL * 15 / 2) /* inactivity timeout */ - -struct bare_shunt { - policy_prio_t policy_prio; - ip_subnet ours; - ip_subnet his; - ip_said said; - int transport_proto; - unsigned long count; - time_t last_activity; - char *why; - struct bare_shunt *next; -}; - -static struct bare_shunt *bare_shunts = NULL; - -#ifdef DEBUG -static void -DBG_bare_shunt(const char *op, const struct bare_shunt *bs) -{ - DBG(DBG_KLIPS, - { - int ourport = ntohs(portof(&(bs)->ours.addr)); - int hisport = ntohs(portof(&(bs)->his.addr)); - char ourst[SUBNETTOT_BUF]; - char hist[SUBNETTOT_BUF]; - char sat[SATOT_BUF]; - char prio[POLICY_PRIO_BUF]; - - subnettot(&(bs)->ours, 0, ourst, sizeof(ourst)); - subnettot(&(bs)->his, 0, hist, sizeof(hist)); - satot(&(bs)->said, 0, sat, sizeof(sat)); - fmt_policy_prio(bs->policy_prio, prio); - DBG_log("%s bare shunt %p %s:%d -> %s:%d => %s:%d %s %s" - , op, (const void *)(bs), ourst, ourport, hist, hisport - , sat, (bs)->transport_proto, prio, (bs)->why); - }); -} -#else /* !DEBUG */ -#define DBG_bare_shunt(op, bs) {} -#endif /* !DEBUG */ - -/* The orphaned_holds table records %holds for which we - * scan_proc_shunts found no representation of in any connection. - * The corresponding ACQUIRE message might have been lost. - */ -struct eroute_info *orphaned_holds = NULL; - -/* forward declaration */ -static bool shunt_eroute(struct connection *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); - -bool no_klips = FALSE; /* don't actually use KLIPS */ - -static const struct pfkey_proto_info null_proto_info[2] = { - { - proto: IPPROTO_ESP, - encapsulation: ENCAPSULATION_MODE_TRANSPORT, - reqid: 0 - }, - { - proto: 0, - encapsulation: 0, - reqid: 0 - } -}; - -void -record_and_initiate_opportunistic(const ip_subnet *ours - , const ip_subnet *his - , int transport_proto - , const char *why) -{ - passert(samesubnettype(ours, his)); - - /* Add to bare shunt list. - * We need to do this because the shunt was installed by KLIPS - * which can't do this itself. - */ - { - struct bare_shunt *bs = alloc_thing(struct bare_shunt, "bare shunt"); - - bs->why = clone_str(why, "story for bare shunt"); - bs->ours = *ours; - bs->his = *his; - bs->transport_proto = transport_proto; - bs->policy_prio = BOTTOM_PRIO; - - bs->said.proto = SA_INT; - bs->said.spi = htonl(SPI_HOLD); - bs->said.dst = *aftoinfo(subnettypeof(ours))->any; - - bs->count = 0; - bs->last_activity = now(); - - bs->next = bare_shunts; - bare_shunts = bs; - DBG_bare_shunt("add", bs); - } - - /* actually initiate opportunism */ - { - ip_address src, dst; - - networkof(ours, &src); - networkof(his, &dst); - initiate_opportunistic(&src, &dst, transport_proto, TRUE, NULL_FD); - } - - /* if present, remove from orphaned_holds list. - * NOTE: we do this last in case ours or his is a pointer into a member. - */ - { - struct eroute_info **pp, *p; - - for (pp = &orphaned_holds; (p = *pp) != NULL; pp = &p->next) - { - if (samesubnet(ours, &p->ours) - && samesubnet(his, &p->his) - && transport_proto == p->transport_proto - && portof(&ours->addr) == portof(&p->ours.addr) - && portof(&his->addr) == portof(&p->his.addr)) - { - *pp = p->next; - pfree(p); - break; - } - } - } -} - -#endif /* KLIPS */ - -static unsigned get_proto_reqid(unsigned base, int proto) -{ - switch (proto) - { - default: - case IPPROTO_COMP: - base++; - /* fall through */ - case IPPROTO_ESP: - base++; - /* fall through */ - case IPPROTO_AH: - break; - } - - return base; -} - -/* Generate Unique SPI numbers. - * - * The specs say that the number must not be less than IPSEC_DOI_SPI_MIN. - * Pluto generates numbers not less than IPSEC_DOI_SPI_OUR_MIN, - * reserving numbers in between for manual keying (but we cannot so - * restrict numbers generated by our peer). - * XXX This should be replaced by a call to the kernel when - * XXX we get an API. - * The returned SPI is in network byte order. - * We use a random number as the initial SPI so that there is - * a good chance that different Pluto instances will choose - * different SPIs. This is good for two reasons. - * - the keying material for the initiator and responder only - * differs if the SPIs differ. - * - if Pluto is restarted, it would otherwise recycle the SPI - * numbers and confuse everything. When the kernel generates - * SPIs, this will no longer matter. - * We then allocate numbers sequentially. Thus we don't have to - * check if the number was previously used (assuming that no - * SPI lives longer than 4G of its successors). - */ -ipsec_spi_t -get_ipsec_spi(ipsec_spi_t avoid, int proto, struct spd_route *sr, bool tunnel) -{ - static ipsec_spi_t spi = 0; /* host order, so not returned directly! */ - char text_said[SATOT_BUF]; - - set_text_said(text_said, &sr->this.host_addr, 0, proto); - - if (kernel_ops->get_spi) - return kernel_ops->get_spi(&sr->that.host_addr - , &sr->this.host_addr, proto, tunnel - , get_proto_reqid(sr->reqid, proto) - , IPSEC_DOI_SPI_OUR_MIN, 0xffffffff - , text_said); - - spi++; - while (spi < IPSEC_DOI_SPI_OUR_MIN || spi == ntohl(avoid)) - get_rnd_bytes((u_char *)&spi, sizeof(spi)); - - DBG(DBG_CONTROL, - { - ipsec_spi_t spi_net = htonl(spi); - - DBG_dump("generate SPI:", (u_char *)&spi_net, sizeof(spi_net)); - }); - - return htonl(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. - * Modelled on get_ipsec_spi, but range is more limited: - * 256-61439. - * If we can't find one easily, return 0 (a bad SPI, - * no matter what order) indicating failure. - */ -ipsec_spi_t -get_my_cpi(struct spd_route *sr, bool tunnel) -{ - static cpi_t - first_busy_cpi = 0, - latest_cpi; - char text_said[SATOT_BUF]; - - set_text_said(text_said, &sr->this.host_addr, 0, IPPROTO_COMP); - - if (kernel_ops->get_spi) - return kernel_ops->get_spi(&sr->that.host_addr - , &sr->this.host_addr, IPPROTO_COMP, tunnel - , get_proto_reqid(sr->reqid, IPPROTO_COMP) - , IPCOMP_FIRST_NEGOTIATED, IPCOMP_LAST_NEGOTIATED - , text_said); - - while (!(IPCOMP_FIRST_NEGOTIATED <= first_busy_cpi && first_busy_cpi < IPCOMP_LAST_NEGOTIATED)) - { - get_rnd_bytes((u_char *)&first_busy_cpi, sizeof(first_busy_cpi)); - latest_cpi = first_busy_cpi; - } - - latest_cpi++; - - if (latest_cpi == first_busy_cpi) - find_my_cpi_gap(&latest_cpi, &first_busy_cpi); - - if (latest_cpi > IPCOMP_LAST_NEGOTIATED) - latest_cpi = IPCOMP_FIRST_NEGOTIATED; - - return htonl((ipsec_spi_t)latest_cpi); -} - -/* 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(struct connection *c, struct spd_route *sr, 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], - secure_myid_str[BUF_LEN] = "", - secure_peerid_str[BUF_LEN] = "", - secure_peerca_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 (addrbytesptr(&sr->this.host_srcip, NULL) - && !isanyaddr(&sr->this.host_srcip)) - { - char *n; - - strcpy(srcip_str, "PLUTO_MY_SOURCEIP='"); - n = srcip_str + strlen(srcip_str); - - addrtot(&sr->this.host_srcip, 0 - ,n , sizeof(srcip_str)-strlen(srcip_str)); - strncat(srcip_str, "' ", sizeof(srcip_str)); - } - - addrtot(&sr->this.host_addr, 0, me_str, sizeof(me_str)); - idtoa(&sr->this.id, myid_str, sizeof(myid_str)); - 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)); - - addrtot(&sr->that.host_addr, 0, peer_str, sizeof(peer_str)); - idtoa(&sr->that.id, peerid_str, sizeof(peerid_str)); - 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; - int pathlen; - - if (key->alg == PUBKEY_ALG_RSA && same_id(&sr->that.id, &key->id) - && trusted_ca(key->issuer, sr->that.ca, &pathlen)) - { - dntoa_or_null(peerca_str, BUF_LEN, key->issuer, ""); - escape_metachar(peerca_str, secure_peerca_str, sizeof(secure_peerca_str)); - 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" /* actual script */ - , verb, verb_suffix - , c->name - , nexthop_str - , c->interface->vname - , sr->this.hostaccess? "PLUTO_HOST_ACCESS='1' " : "" - , sr->reqid + 1 /* ESP requid */ - , 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 - , 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)); - -#ifdef KLIPS - if (!no_klips) - { - /* 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; - } - } - } -#endif /* KLIPS */ - 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(struct connection *c) -{ - struct spd_route *esr, *rosr; - struct connection *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 (!no_klips -#ifdef NAT_TRAVERSAL - && c->spd.this.host_port != NAT_T_IKE_FLOAT_PORT -#endif - && 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 */ - } - -#ifdef KLIPS - /* if there is an eroute for another connection, there is a problem */ - if (ero != NULL && ero != c) - { - struct connection *ero2, *ero_top; - struct connection *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 FALSE; /* another connection already using the eroute */ - } - } -#endif /* KLIPS */ - return route_easy; -} - -bool -trap_connection(struct connection *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(struct connection *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); -#ifdef KLIPS - shunt_eroute(c, sr, RT_UNROUTED, ERO_DELETE, "delete"); -#endif - } - - 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, "unroute"); - } -} - - -#ifdef KLIPS - -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); -} - -/* find an entry in the bare_shunt table. - * Trick: return a pointer to the pointer to the entry; - * this allows the entry to be deleted. - */ -static struct bare_shunt ** -bare_shunt_ptr(const ip_subnet *ours, const ip_subnet *his, int transport_proto) -{ - struct bare_shunt *p, **pp; - - for (pp = &bare_shunts; (p = *pp) != NULL; pp = &p->next) - { - if (samesubnet(ours, &p->ours) - && samesubnet(his, &p->his) - && transport_proto == p->transport_proto - && portof(&ours->addr) == portof(&p->ours.addr) - && portof(&his->addr) == portof(&p->his.addr)) - return pp; - } - return NULL; -} - -/* free a bare_shunt entry, given a pointer to the pointer */ -static void -free_bare_shunt(struct bare_shunt **pp) -{ - if (pp == NULL) - { - DBG(DBG_CONTROL, - DBG_log("delete bare shunt: null pointer") - ) - } - else - { - struct bare_shunt *p = *pp; - - *pp = p->next; - DBG_bare_shunt("delete", p); - pfree(p->why); - pfree(p); - } -} - -void -show_shunt_status(void) -{ - struct bare_shunt *bs; - - for (bs = bare_shunts; bs != NULL; bs = bs->next) - { - /* Print interesting fields. Ignore count and last_active. */ - - int ourport = ntohs(portof(&bs->ours.addr)); - int hisport = ntohs(portof(&bs->his.addr)); - char ourst[SUBNETTOT_BUF]; - char hist[SUBNETTOT_BUF]; - char sat[SATOT_BUF]; - char prio[POLICY_PRIO_BUF]; - - subnettot(&(bs)->ours, 0, ourst, sizeof(ourst)); - subnettot(&(bs)->his, 0, hist, sizeof(hist)); - satot(&(bs)->said, 0, sat, sizeof(sat)); - fmt_policy_prio(bs->policy_prio, prio); - - whack_log(RC_COMMENT, "%s:%d -> %s:%d => %s:%d %s %s" - , ourst, ourport, hist, hisport, sat, bs->transport_proto - , prio, bs->why); - } - if (bare_shunts != NULL) - whack_log(RC_COMMENT, BLANK_FORMAT); /* spacer */ -} - -/* 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 - , ipsec_spi_t spi - , unsigned int proto - , unsigned int satype - , unsigned int transport_proto - , const struct pfkey_proto_info *proto_info - , time_t use_lifetime - , unsigned int op - , const char *opname USED_BY_DEBUG) -{ - char text_said[SATOT_BUF]; - - set_text_said(text_said, that_host, spi, proto); - - DBG(DBG_CONTROL | DBG_KLIPS, - { - 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); - }); - - return kernel_ops->raw_eroute(this_host, this_client - , that_host, that_client, spi, satype, transport_proto, proto_info - , use_lifetime, op, text_said); -} - -/* test to see if %hold remains */ -bool -has_bare_hold(const ip_address *src, const ip_address *dst, int transport_proto) -{ - ip_subnet this_client, that_client; - struct bare_shunt **bspp; - - passert(addrtypeof(src) == addrtypeof(dst)); - happy(addrtosubnet(src, &this_client)); - happy(addrtosubnet(dst, &that_client)); - bspp = bare_shunt_ptr(&this_client, &that_client, transport_proto); - return bspp != NULL - && (*bspp)->said.proto == SA_INT && (*bspp)->said.spi == htonl(SPI_HOLD); -} - - -/* Replace (or delete) a shunt that is in the bare_shunts table. - * Issues the PF_KEY commands and updates the bare_shunts table. - */ -bool -replace_bare_shunt(const ip_address *src, const ip_address *dst - , policy_prio_t policy_prio - , ipsec_spi_t shunt_spi /* in host order! */ - , bool repl /* if TRUE, replace; if FALSE, delete */ - , unsigned int transport_proto - , const char *why) -{ - ip_subnet this_client, that_client; - ip_subnet this_broad_client, that_broad_client; - const ip_address *null_host = aftoinfo(addrtypeof(src))->any; - - passert(addrtypeof(src) == addrtypeof(dst)); - happy(addrtosubnet(src, &this_client)); - happy(addrtosubnet(dst, &that_client)); - this_broad_client = this_client; - that_broad_client = that_client; - setportof(0, &this_broad_client.addr); - setportof(0, &that_broad_client.addr); - - if (repl) - { - struct bare_shunt **bs_pp = bare_shunt_ptr(&this_broad_client - , &that_broad_client, 0); - - /* is there already a broad host-to-host bare shunt? */ - if (bs_pp == NULL) - { - if (raw_eroute(null_host, &this_broad_client, null_host, &that_broad_client - , htonl(shunt_spi), SA_INT, SADB_X_SATYPE_INT - , 0, null_proto_info - , SHUNT_PATIENCE, ERO_ADD, why)) - { - struct bare_shunt *bs = alloc_thing(struct bare_shunt, "bare shunt"); - - bs->ours = this_broad_client; - bs->his = that_broad_client; - bs->transport_proto = 0; - bs->said.proto = SA_INT; - bs->why = clone_str(why, "bare shunt story"); - bs->policy_prio = policy_prio; - bs->said.spi = htonl(shunt_spi); - bs->said.dst = *null_host; - bs->count = 0; - bs->last_activity = now(); - bs->next = bare_shunts; - bare_shunts = bs; - DBG_bare_shunt("add", bs); - } - } - shunt_spi = SPI_HOLD; - } - - if (raw_eroute(null_host, &this_client, null_host, &that_client - , htonl(shunt_spi), SA_INT, SADB_X_SATYPE_INT - , transport_proto, null_proto_info - , SHUNT_PATIENCE, ERO_DELETE, why)) - { - struct bare_shunt **bs_pp = bare_shunt_ptr(&this_client, &that_client - , transport_proto); - - /* delete bare eroute */ - free_bare_shunt(bs_pp); - return TRUE; - } - else - { - return FALSE; - } -} - -static bool -eroute_connection(struct spd_route *sr -, ipsec_spi_t spi, unsigned int proto, unsigned int satype -, const struct pfkey_proto_info *proto_info -, unsigned int op, const char *opname) -{ - const ip_address *peer = &sr->that.host_addr; - char buf2[256]; - - snprintf(buf2, sizeof(buf2) - , "eroute_connection %s", opname); - - if (proto == SA_INT) - peer = aftoinfo(addrtypeof(peer))->any; - - return raw_eroute(&sr->this.host_addr, &sr->this.client - , peer - , &sr->that.client - , spi, proto, satype - , sr->this.protocol, proto_info, 0, op, buf2); -} - -/* assign a bare hold to a connection */ - -bool -assign_hold(struct connection *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, not the narrow one. - * 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. - * Once the broad %hold is in place, delete the narrow one. - */ - if (rn != ro) - { - if (erouted(ro) - ? !eroute_connection(sr, htonl(SPI_HOLD), SA_INT, SADB_X_SATYPE_INT - , null_proto_info - , ERO_REPLACE, "replace %trap with broad %hold") - : !eroute_connection(sr, htonl(SPI_HOLD), SA_INT, SADB_X_SATYPE_INT - , null_proto_info - , ERO_ADD, "add broad %hold")) - { - return FALSE; - } - } - if (!replace_bare_shunt(src, dst, BOTTOM_PRIO, SPI_HOLD, FALSE - , transport_proto, "delete narrow %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 = 0; - u_int inner_satype = 0; - ipsec_spi_t inner_spi = 0; - struct pfkey_proto_info proto_info[4]; - int i; - bool tunnel; - - /* figure out the SPI and protocol (in two forms) - * for the innermost transformation. - */ - - i = sizeof(proto_info) / sizeof(proto_info[0]) - 1; - proto_info[i].proto = 0; - tunnel = FALSE; - - if (st->st_ah.present) - { - inner_spi = st->st_ah.attrs.spi; - inner_proto = SA_AH; - inner_satype = SADB_SATYPE_AH; - - i--; - proto_info[i].proto = IPPROTO_AH; - proto_info[i].encapsulation = st->st_ah.attrs.encapsulation; - tunnel |= proto_info[i].encapsulation == ENCAPSULATION_MODE_TUNNEL; - proto_info[i].reqid = sr->reqid; - } - - if (st->st_esp.present) - { - inner_spi = st->st_esp.attrs.spi; - inner_proto = SA_ESP; - inner_satype = SADB_SATYPE_ESP; - - i--; - proto_info[i].proto = IPPROTO_ESP; - proto_info[i].encapsulation = st->st_esp.attrs.encapsulation; - tunnel |= proto_info[i].encapsulation == ENCAPSULATION_MODE_TUNNEL; - proto_info[i].reqid = sr->reqid + 1; - } - - if (st->st_ipcomp.present) - { - inner_spi = st->st_ipcomp.attrs.spi; - inner_proto = SA_COMP; - inner_satype = SADB_X_SATYPE_COMP; - - i--; - proto_info[i].proto = IPPROTO_COMP; - proto_info[i].encapsulation = st->st_ipcomp.attrs.encapsulation; - tunnel |= proto_info[i].encapsulation == ENCAPSULATION_MODE_TUNNEL; - proto_info[i].reqid = sr->reqid + 2; - } - - if (i == sizeof(proto_info) / sizeof(proto_info[0]) - 1) - { - impossible(); /* no transform at all! */ - } - - if (tunnel) - { - int j; - - inner_spi = st->st_tunnel_out_spi; - inner_proto = SA_IPIP; - inner_satype = SADB_X_SATYPE_IPIP; - - proto_info[i].encapsulation = ENCAPSULATION_MODE_TUNNEL; - for (j = i + 1; proto_info[j].proto; j++) - { - proto_info[j].encapsulation = ENCAPSULATION_MODE_TRANSPORT; - } - } - - return eroute_connection(sr - , inner_spi, inner_proto, inner_satype, proto_info + i - , op, opname); -} - -/* compute a (host-order!) SPI to implement the policy in connection c */ -ipsec_spi_t -shunt_policy_spi(struct connection *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(struct connection *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); - bool ok; - - 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; - struct connection *ue = eclipsed(c, &esr); - - if (ue != NULL) - { - esr->routing = RT_ROUTED_PROSPECTIVE; - return shunt_eroute(ue, esr - , RT_ROUTED_PROSPECTIVE, ERO_REPLACE, "restoring eclipsed"); - } - } - - ok = TRUE; - if (kernel_ops->inbound_eroute) - { - ok = raw_eroute(&c->spd.that.host_addr, &c->spd.that.client - , &c->spd.this.host_addr, &c->spd.this.client - , htonl(spi), SA_INT, SADB_X_SATYPE_INT - , 0, null_proto_info, 0 - , op | (SADB_X_SAFLAGS_INFLOW << ERO_FLAG_SHIFT), opname); - } - return eroute_connection(sr, htonl(spi), SA_INT, SADB_X_SATYPE_INT - , null_proto_info, op, opname) && ok; -} - - -/* - * This is only called when s is a likely SAID with trailing protocol i.e. - * it has the form :- - * - * %<keyword>:p - * <ip-proto><spi>@a.b.c.d:p - * - * The task here is to remove the ":p" part so that the rest can be read - * by another routine. - */ -static const char * -read_proto(const char * s, size_t * len, int * transport_proto) -{ - const char * p; - const char * ugh; - unsigned long proto; - size_t l; - - l = *len; - p = memchr(s, ':', l); - if (p == 0) { - *transport_proto = 0; - return 0; - } - ugh = ttoul(p+1, l-((p-s)+1), 10, &proto); - if (ugh != 0) - return ugh; - if (proto > 65535) - return "protocol number is too large, legal range is 0-65535"; - *len = p-s; - *transport_proto = proto; - return 0; -} - - -/* scan /proc/net/ipsec_eroute every once in a while, looking for: - * - * - %hold shunts of which Pluto isn't aware. This situation could - * be caused by lost ACQUIRE messages. When found, they will - * added to orphan_holds. This in turn will lead to Opportunistic - * initiation. - * - * - other kinds of shunts that haven't been used recently. These will be - * deleted. They represent OE failures. - * - * - recording recent uses of tunnel eroutes so that rekeying decisions - * can be made for OE connections. - * - * Here are some sample lines: - * 10 10.3.2.1.0/24 -> 0.0.0.0/0 => %trap - * 259 10.3.2.1.115/32 -> 10.19.75.161/32 => tun0x1002@10.19.75.145 - * 71 10.44.73.97/32 -> 0.0.0.0/0 => %trap - * 4119 10.44.73.97/32 -> 10.114.121.41/32 => %pass - * Newer versions of KLIPS start each line with a 32-bit packet count. - * If available, the count is used to detect whether a %pass shunt is in use. - * - * NOTE: execution time is quadratic in the number of eroutes since the - * searching for each is sequential. If this becomes a problem, faster - * searches could be implemented (hash or radix tree, for example). - */ -void -scan_proc_shunts(void) -{ - static const char procname[] = "/proc/net/ipsec_eroute"; - FILE *f; - time_t nw = now(); - int lino; - struct eroute_info *expired = NULL; - - event_schedule(EVENT_SHUNT_SCAN, SHUNT_SCAN_INTERVAL, NULL); - - DBG(DBG_CONTROL, - DBG_log("scanning for shunt eroutes") - ) - - /* free any leftover entries: they will be refreshed if still current */ - while (orphaned_holds != NULL) - { - struct eroute_info *p = orphaned_holds; - - orphaned_holds = p->next; - pfree(orphaned_holds); - } - - /* decode the /proc file. Don't do anything strenuous to it - * (certainly no PF_KEY stuff) to minimize the chance that it - * might change underfoot. - */ - - f = fopen(procname, "r"); - if (f == NULL) - return; - - /* for each line... */ - for (lino = 1; ; lino++) - { - unsigned char buf[1024]; /* should be big enough */ - chunk_t field[10]; /* 10 is loose upper bound */ - chunk_t *ff = NULL; /* fixed fields (excluding optional count) */ - int fi; - struct eroute_info eri; - char *cp; - err_t context = "" - , ugh = NULL; - - cp = fgets(buf, sizeof(buf), f); - if (cp == NULL) - break; - - /* break out each field - * Note: if there are too many fields, just stop; - * it will be diagnosed a little later. - */ - for (fi = 0; fi < (int)elemsof(field); fi++) - { - static const char sep[] = " \t\n"; /* field-separating whitespace */ - size_t w; - - cp += strspn(cp, sep); /* find start of field */ - w = strcspn(cp, sep); /* find width of field */ - setchunk(field[fi], cp, w); - cp += w; - if (w == 0) - break; - } - - /* This odd do-hickey is to share error reporting code. - * A break will get to that common code. The setting - * of "ugh" and "context" parameterize it. - */ - do { - /* Old entries have no packet count; new ones do. - * check if things are as they should be. - */ - if (fi == 5) - ff = &field[0]; /* old form, with no count */ - else if (fi == 6) - ff = &field[1]; /* new form, with count */ - else - { - ugh = "has wrong number of fields"; - break; - } - - if (ff[1].len != 2 - || strncmp(ff[1].ptr, "->", 2) != 0 - || ff[3].len != 2 - || strncmp(ff[3].ptr, "=>", 2) != 0) - { - ugh = "is missing -> or =>"; - break; - } - - /* actually digest fields of interest */ - - /* packet count */ - - eri.count = 0; - if (ff != field) - { - context = "count field is malformed: "; - ugh = ttoul(field[0].ptr, field[0].len, 10, &eri.count); - if (ugh != NULL) - break; - } - - /* our client */ - - context = "source subnet field malformed: "; - ugh = ttosubnet(ff[0].ptr, ff[0].len, AF_INET, &eri.ours); - if (ugh != NULL) - break; - - /* his client */ - - context = "destination subnet field malformed: "; - ugh = ttosubnet(ff[2].ptr, ff[2].len, AF_INET, &eri.his); - if (ugh != NULL) - break; - - /* SAID */ - - context = "SA ID field malformed: "; - ugh = read_proto(ff[4].ptr, &ff[4].len, &eri.transport_proto); - if (ugh != NULL) - break; - ugh = ttosa(ff[4].ptr, ff[4].len, &eri.said); - } while (FALSE); - - if (ugh != NULL) - { - plog("INTERNAL ERROR: %s line %d %s%s" - , procname, lino, context, ugh); - continue; /* ignore rest of line */ - } - - /* Now we have decoded eroute, let's consider it. - * For shunt eroutes: - * - * %hold: if not known, add to orphaned_holds list for initiation - * because ACQUIRE might have been lost. - * - * %pass, %drop, %reject: determine if idle; if so, blast it away. - * Can occur bare (if DNS provided insufficient information) - * or with a connection (failure context). - * Could even be installed by ipsec manual. - * - * %trap: always welcome. - * - * For other eroutes: find state and record count change - */ - if (eri.said.proto == SA_INT) - { - /* shunt eroute */ - switch (ntohl(eri.said.spi)) - { - case SPI_HOLD: - if (bare_shunt_ptr(&eri.ours, &eri.his, eri.transport_proto) == NULL - && shunt_owner(&eri.ours, &eri.his) == NULL) - { - int ourport = ntohs(portof(&eri.ours.addr)); - int hisport = ntohs(portof(&eri.his.addr)); - char ourst[SUBNETTOT_BUF]; - char hist[SUBNETTOT_BUF]; - char sat[SATOT_BUF]; - - subnettot(&eri.ours, 0, ourst, sizeof(ourst)); - subnettot(&eri.his, 0, hist, sizeof(hist)); - satot(&eri.said, 0, sat, sizeof(sat)); - - DBG(DBG_CONTROL, - DBG_log("add orphaned shunt %s:%d -> %s:%d => %s:%d" - , ourst, ourport, hist, hisport, sat, eri.transport_proto) - ) - eri.next = orphaned_holds; - orphaned_holds = clone_thing(eri, "orphaned %hold"); - } - break; - - case SPI_PASS: - case SPI_DROP: - case SPI_REJECT: - /* nothing sensible to do if we don't have counts */ - if (ff != field) - { - struct bare_shunt **bs_pp - = bare_shunt_ptr(&eri.ours, &eri.his, eri.transport_proto); - - if (bs_pp != NULL) - { - struct bare_shunt *bs = *bs_pp; - - if (eri.count != bs->count) - { - bs->count = eri.count; - bs->last_activity = nw; - } - else if (nw - bs->last_activity > SHUNT_PATIENCE) - { - eri.next = expired; - expired = clone_thing(eri, "expired %pass"); - } - } - } - break; - - case SPI_TRAP: - break; - - default: - bad_case(ntohl(eri.said.spi)); - } - } - else - { - /* regular (non-shunt) eroute */ - state_eroute_usage(&eri.ours, &eri.his, eri.count, nw); - } - } /* for each line */ - fclose(f); - - /* Now that we've finished processing the /proc file, - * it is safe to delete the expired %pass shunts. - */ - while (expired != NULL) - { - struct eroute_info *p = expired; - ip_address src, dst; - - networkof(&p->ours, &src); - networkof(&p->his, &dst); - (void) replace_bare_shunt(&src, &dst - , BOTTOM_PRIO /* not used because we are deleting. This value is a filler */ - , SPI_PASS /* not used because we are deleting. This value is a filler */ - , FALSE, p->transport_proto, "delete expired bare shunts"); - expired = p->next; - pfree(p); - } -} - -static bool -del_spi(ipsec_spi_t spi, int proto -, const ip_address *src, const ip_address *dest) -{ - char text_said[SATOT_BUF]; - struct kernel_sa sa; - - set_text_said(text_said, dest, spi, proto); - - DBG(DBG_KLIPS, DBG_log("delete %s", text_said)); - - memset(&sa, 0, sizeof(sa)); - sa.spi = spi; - sa.proto = proto; - sa.src = src; - sa.dst = dest; - sa.text_said = text_said; - - return kernel_ops->del_sa(&sa); -} - -/* Setup a pair of SAs. Code taken from setsa.c and spigrp.c, in - * ipsec-0.5. - */ - -static bool -setup_half_ipsec_sa(struct state *st, bool inbound) -{ - /* Build an inbound or outbound SA */ - - struct connection *c = st->st_connection; - ip_subnet src, dst; - ip_subnet src_client, dst_client; - ipsec_spi_t inner_spi = 0; - u_int proto = 0; - u_int satype = SADB_SATYPE_UNSPEC; - bool replace; - - /* SPIs, saved for spigrouping or undoing, if necessary */ - struct kernel_sa - said[EM_MAXRELSPIS], - *said_next = said; - - char text_said[SATOT_BUF]; - int encapsulation; - - replace = inbound && (kernel_ops->get_spi != NULL); - - src.maskbits = 0; - dst.maskbits = 0; - - if (inbound) - { - src.addr = c->spd.that.host_addr; - dst.addr = c->spd.this.host_addr; - src_client = c->spd.that.client; - dst_client = c->spd.this.client; - } - else - { - src.addr = c->spd.this.host_addr, - dst.addr = c->spd.that.host_addr; - src_client = c->spd.this.client; - dst_client = c->spd.that.client; - } - - encapsulation = ENCAPSULATION_MODE_TRANSPORT; - 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) - { - encapsulation = ENCAPSULATION_MODE_TUNNEL; - } - - memset(said, 0, sizeof(said)); - - /* If we are tunnelling, set up IP in IP pseudo SA */ - - if (kernel_ops->inbound_eroute) - { - inner_spi = 256; - proto = SA_IPIP; - satype = SADB_SATYPE_UNSPEC; - } - else if (encapsulation == ENCAPSULATION_MODE_TUNNEL) - { - /* XXX hack alert -- we SHOULD NOT HAVE TO HAVE A DIFFERENT SPI - * XXX FOR IP-in-IP ENCAPSULATION! - */ - - ipsec_spi_t ipip_spi; - - /* Allocate an SPI for the tunnel. - * Since our peer will never see this, - * and it comes from its own number space, - * it is purely a local implementation wart. - */ - { - static ipsec_spi_t last_tunnel_spi = IPSEC_DOI_SPI_OUR_MIN; - - ipip_spi = htonl(++last_tunnel_spi); - if (inbound) - st->st_tunnel_in_spi = ipip_spi; - else - st->st_tunnel_out_spi = ipip_spi; - } - - set_text_said(text_said - , &c->spd.that.host_addr, ipip_spi, SA_IPIP); - - said_next->src = &src.addr; - said_next->dst = &dst.addr; - said_next->src_client = &src_client; - said_next->dst_client = &dst_client; - said_next->spi = ipip_spi; - said_next->satype = SADB_X_SATYPE_IPIP; - said_next->text_said = text_said; - - if (!kernel_ops->add_sa(said_next, replace)) - goto fail; - - said_next++; - - inner_spi = ipip_spi; - proto = SA_IPIP; - satype = SADB_X_SATYPE_IPIP; - } - - /* 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; - unsigned compalg; - - switch (st->st_ipcomp.attrs.transid) - { - case IPCOMP_DEFLATE: - compalg = SADB_X_CALG_DEFLATE; - break; - - default: - loglog(RC_LOG_SERIOUS, "IPCOMP transform %s not implemented" - , enum_name(&ipcomp_transformid_names, st->st_ipcomp.attrs.transid)); - goto fail; - } - - set_text_said(text_said, &dst.addr, ipcomp_spi, SA_COMP); - - said_next->src = &src.addr; - said_next->dst = &dst.addr; - said_next->src_client = &src_client; - said_next->dst_client = &dst_client; - said_next->spi = ipcomp_spi; - said_next->satype = SADB_X_SATYPE_COMP; - said_next->compalg = compalg; - said_next->encapsulation = encapsulation; - said_next->reqid = c->spd.reqid + 2; - said_next->text_said = text_said; - - if (!kernel_ops->add_sa(said_next, replace)) - goto fail; - - said_next++; - - encapsulation = ENCAPSULATION_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; - const struct esp_info *ei; - u_int16_t key_len; - - static const struct esp_info esp_info[] = { - { ESP_NULL, AUTH_ALGORITHM_HMAC_MD5, - 0, HMAC_MD5_KEY_LEN, - SADB_EALG_NULL, SADB_AALG_MD5_HMAC }, - { ESP_NULL, AUTH_ALGORITHM_HMAC_SHA1, - 0, HMAC_SHA1_KEY_LEN, - SADB_EALG_NULL, SADB_AALG_SHA1_HMAC }, - - { ESP_DES, AUTH_ALGORITHM_NONE, - DES_CBC_BLOCK_SIZE, 0, - SADB_EALG_DES_CBC, SADB_AALG_NONE }, - { ESP_DES, AUTH_ALGORITHM_HMAC_MD5, - DES_CBC_BLOCK_SIZE, HMAC_MD5_KEY_LEN, - SADB_EALG_DES_CBC, SADB_AALG_MD5_HMAC }, - { ESP_DES, AUTH_ALGORITHM_HMAC_SHA1, - DES_CBC_BLOCK_SIZE, - HMAC_SHA1_KEY_LEN, SADB_EALG_DES_CBC, SADB_AALG_SHA1_HMAC }, - - { ESP_3DES, AUTH_ALGORITHM_NONE, - DES_CBC_BLOCK_SIZE * 3, 0, - SADB_EALG_3DES_CBC, SADB_AALG_NONE }, - { ESP_3DES, AUTH_ALGORITHM_HMAC_MD5, - DES_CBC_BLOCK_SIZE * 3, HMAC_MD5_KEY_LEN, - SADB_EALG_3DES_CBC, SADB_AALG_MD5_HMAC }, - { ESP_3DES, AUTH_ALGORITHM_HMAC_SHA1, - DES_CBC_BLOCK_SIZE * 3, HMAC_SHA1_KEY_LEN, - SADB_EALG_3DES_CBC, SADB_AALG_SHA1_HMAC }, - }; - -#ifdef NAT_TRAVERSAL - u_int8_t natt_type = 0; - u_int16_t natt_sport = 0, natt_dport = 0; - ip_address natt_oa; - - if (st->nat_traversal & NAT_T_DETECTED) { - natt_type = (st->nat_traversal & NAT_T_WITH_PORT_FLOATING) ? - ESPINUDP_WITH_NON_ESP : ESPINUDP_WITH_NON_IKE; - natt_sport = inbound? c->spd.that.host_port : c->spd.this.host_port; - natt_dport = inbound? c->spd.this.host_port : c->spd.that.host_port; - natt_oa = st->nat_oa; - } -#endif - - for (ei = esp_info; ; ei++) - { - if (ei == &esp_info[elemsof(esp_info)]) - { - /* Check for additional kernel alg */ -#ifndef NO_KERNEL_ALG - if ((ei=kernel_alg_esp_info(st->st_esp.attrs.transid, - st->st_esp.attrs.auth))!=NULL) { - break; - } -#endif - - /* note: enum_show may use a static buffer, so two - * calls in one printf would be a mistake. - * enum_name does the same job, without a static buffer, - * assuming the name will be found. - */ - loglog(RC_LOG_SERIOUS, "ESP transform %s / auth %s not implemented yet" - , enum_name(&esp_transformid_names, st->st_esp.attrs.transid) - , enum_name(&auth_alg_names, st->st_esp.attrs.auth)); - goto fail; - } - - if (st->st_esp.attrs.transid == ei->transid - && st->st_esp.attrs.auth == ei->auth) - break; - } - - 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 passed key_len=%d > %d", - enum_name(&esp_transformid_names, st->st_esp.attrs.transid), - (int)key_len, (int)ei->enckeylen); - goto fail; - } - } else { - key_len = ei->enckeylen; - } - /* Grrrrr.... f*cking 7 bits jurassic algos */ - - /* 168 bits in kernel, need 192 bits for keymat_len */ - if (ei->transid == ESP_3DES && key_len == 21) - key_len = 24; - - /* 56 bits in kernel, need 64 bits for keymat_len */ - if (ei->transid == ESP_DES && key_len == 7) - key_len = 8; - - /* divide up keying material */ - /* passert(st->st_esp.keymat_len == ei->enckeylen + ei->authkeylen); */ - DBG(DBG_KLIPS|DBG_CONTROL|DBG_PARSING, - if(st->st_esp.keymat_len != key_len + ei->authkeylen) - DBG_log("keymat_len=%d key_len=%d authkeylen=%d", - st->st_esp.keymat_len, (int)key_len, (int)ei->authkeylen); - ); - passert(st->st_esp.keymat_len == key_len + ei->authkeylen); - - set_text_said(text_said, &dst.addr, esp_spi, SA_ESP); - - said_next->src = &src.addr; - said_next->dst = &dst.addr; - said_next->src_client = &src_client; - said_next->dst_client = &dst_client; - said_next->spi = esp_spi; - said_next->satype = SADB_SATYPE_ESP; - said_next->replay_window = (kernel_ops->type == KERNEL_TYPE_KLIPS) ? REPLAY_WINDOW : REPLAY_WINDOW_XFRM; - said_next->authalg = ei->authalg; - said_next->authkeylen = ei->authkeylen; - /* said_next->authkey = esp_dst_keymat + ei->enckeylen; */ - said_next->authkey = esp_dst_keymat + key_len; - said_next->encalg = ei->encryptalg; - /* said_next->enckeylen = ei->enckeylen; */ - said_next->enckeylen = key_len; - said_next->enckey = esp_dst_keymat; - said_next->encapsulation = encapsulation; - said_next->reqid = c->spd.reqid + 1; -#ifdef NAT_TRAVERSAL - said_next->natt_sport = natt_sport; - said_next->natt_dport = natt_dport; - said_next->transid = st->st_esp.attrs.transid; - said_next->natt_type = natt_type; - said_next->natt_oa = &natt_oa; -#endif - said_next->text_said = text_said; - - if (!kernel_ops->add_sa(said_next, replace)) - goto fail; - - said_next++; - - encapsulation = ENCAPSULATION_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; - - unsigned char authalg; - - switch (st->st_ah.attrs.auth) - { - case AUTH_ALGORITHM_HMAC_MD5: - authalg = SADB_AALG_MD5_HMAC; - break; - - case AUTH_ALGORITHM_HMAC_SHA1: - authalg = SADB_AALG_SHA1_HMAC; - break; - - default: - loglog(RC_LOG_SERIOUS, "%s not implemented yet" - , enum_show(&auth_alg_names, st->st_ah.attrs.auth)); - goto fail; - } - - set_text_said(text_said, &dst.addr, ah_spi, SA_AH); - - said_next->src = &src.addr; - said_next->dst = &dst.addr; - said_next->src_client = &src_client; - said_next->dst_client = &dst_client; - said_next->spi = ah_spi; - said_next->satype = SADB_SATYPE_AH; - said_next->replay_window = (kernel_ops->type == KERNEL_TYPE_KLIPS) ? REPLAY_WINDOW : REPLAY_WINDOW_XFRM; - said_next->authalg = authalg; - said_next->authkeylen = st->st_ah.keymat_len; - said_next->authkey = ah_dst_keymat; - said_next->encapsulation = encapsulation; - said_next->reqid = c->spd.reqid; - said_next->text_said = text_said; - - if (!kernel_ops->add_sa(said_next, replace)) - goto fail; - - said_next++; - - encapsulation = ENCAPSULATION_MODE_TRANSPORT; - } - - 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) - { - encapsulation = ENCAPSULATION_MODE_TUNNEL; - } - - if (kernel_ops->inbound_eroute ? c->spd.eroute_owner == SOS_NOBODY - : encapsulation == ENCAPSULATION_MODE_TUNNEL) - { - /* If inbound, and policy does not specifie DISABLEARRIVALCHECK, - * tell KLIPS to enforce the IP addresses appropriate for this tunnel. - * Note reversed ends. - * Not much to be done on failure. - */ - if (inbound && (c->policy & POLICY_DISABLEARRIVALCHECK) == 0) - { - struct pfkey_proto_info proto_info[4]; - int i = 0; - - if (st->st_ipcomp.present) - { - proto_info[i].proto = IPPROTO_COMP; - proto_info[i].encapsulation = st->st_ipcomp.attrs.encapsulation; - proto_info[i].reqid = c->spd.reqid + 2; - i++; - } - - if (st->st_esp.present) - { - proto_info[i].proto = IPPROTO_ESP; - proto_info[i].encapsulation = st->st_esp.attrs.encapsulation; - proto_info[i].reqid = c->spd.reqid + 1; - i++; - } - - if (st->st_ah.present) - { - proto_info[i].proto = IPPROTO_AH; - proto_info[i].encapsulation = st->st_ah.attrs.encapsulation; - proto_info[i].reqid = c->spd.reqid; - i++; - } - - proto_info[i].proto = 0; - - if (kernel_ops->inbound_eroute - && encapsulation == ENCAPSULATION_MODE_TUNNEL) - { - proto_info[0].encapsulation = ENCAPSULATION_MODE_TUNNEL; - for (i = 1; proto_info[i].proto; i++) - { - proto_info[i].encapsulation = ENCAPSULATION_MODE_TRANSPORT; - } - } - - /* MCR - should be passed a spd_eroute structure here */ - (void) raw_eroute(&c->spd.that.host_addr, &c->spd.that.client - , &c->spd.this.host_addr, &c->spd.this.client - , inner_spi, proto, satype, c->spd.this.protocol - , proto_info, 0 - , ERO_ADD_INBOUND, "add inbound"); - } - } - - /* If there are multiple SPIs, group them. */ - - if (kernel_ops->grp_sa && said_next > &said[1]) - { - struct kernel_sa *s; - - /* group SAs, two at a time, inner to outer (backwards in said[]) - * The grouping is by pairs. So if said[] contains ah esp ipip, - * the grouping would be ipip:esp, esp:ah. - */ - for (s = said; s < said_next-1; s++) - { - char - text_said0[SATOT_BUF], - text_said1[SATOT_BUF]; - - /* group s[1] and s[0], in that order */ - - set_text_said(text_said0, s[0].dst, s[0].spi, s[0].proto); - set_text_said(text_said1, s[1].dst, s[1].spi, s[1].proto); - - DBG(DBG_KLIPS, DBG_log("grouping %s and %s", text_said1, text_said0)); - - s[0].text_said = text_said0; - s[1].text_said = text_said1; - - if (!kernel_ops->grp_sa(s + 1, s)) - goto fail; - } - /* could update said, but it will not be used */ - } - - return TRUE; - -fail: - { - /* undo the done SPIs */ - while (said_next-- != said) - (void) del_spi(said_next->spi, said_next->proto - , &src.addr, said_next->dst); - return FALSE; - } -} - -/* teardown_ipsec_sa is a canibalized version of setup_ipsec_sa */ - -static bool -teardown_half_ipsec_sa(struct state *st, bool inbound) -{ - /* We need to delete AH, ESP, and IP in IP SPIs. - * But if there is more than one, they have been grouped - * so deleting any one will do. So we just delete the - * first one found. It may or may not be the only one. - */ - struct connection *c = st->st_connection; - struct { - unsigned proto; - struct ipsec_proto_info *info; - } protos[4]; - int i; - bool result; - - i = 0; - if (kernel_ops->inbound_eroute && inbound - && c->spd.eroute_owner == SOS_NOBODY) - { - (void) raw_eroute(&c->spd.that.host_addr, &c->spd.that.client - , &c->spd.this.host_addr, &c->spd.this.client - , 256, IPSEC_PROTO_ANY, SADB_SATYPE_UNSPEC, c->spd.this.protocol - , null_proto_info, 0 - , ERO_DEL_INBOUND, "delete inbound"); - } - - if (!kernel_ops->grp_sa) - { - if (st->st_ah.present) - { - protos[i].info = &st->st_ah; - protos[i].proto = SA_AH; - i++; - } - - if (st->st_esp.present) - { - protos[i].info = &st->st_esp; - protos[i].proto = SA_ESP; - i++; - } - - if (st->st_ipcomp.present) - { - protos[i].info = &st->st_ipcomp; - protos[i].proto = SA_COMP; - i++; - } - } - else if (st->st_ah.present) - { - protos[i].info = &st->st_ah; - protos[i].proto = SA_AH; - i++; - } - else if (st->st_esp.present) - { - protos[i].info = &st->st_esp; - protos[i].proto = SA_ESP; - i++; - } - else - { - impossible(); /* neither AH nor ESP in outbound SA bundle! */ - } - protos[i].proto = 0; - - result = TRUE; - for (i = 0; protos[i].proto; i++) - { - unsigned proto = protos[i].proto; - ipsec_spi_t spi; - const ip_address *src, *dst; - - if (inbound) - { - spi = protos[i].info->our_spi; - src = &c->spd.that.host_addr; - dst = &c->spd.this.host_addr; - } - else - { - spi = protos[i].info->attrs.spi; - src = &c->spd.this.host_addr; - dst = &c->spd.that.host_addr; - } - - result &= del_spi(spi, proto, src, 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) -{ - char text_said[SATOT_BUF]; - struct kernel_sa sa; - struct connection *c = st->st_connection; - - *use_time = UNDEFINED_TIME; - - if (kernel_ops->get_sa == NULL || !st->st_esp.present) - return FALSE; - - memset(&sa, 0, sizeof(sa)); - sa.proto = SA_ESP; - - if (inbound) - { - sa.src = &c->spd.that.host_addr; - sa.dst = &c->spd.this.host_addr; - sa.spi = st->st_esp.our_spi; - } - else - { - sa.src = &c->spd.this.host_addr; - sa.dst = &c->spd.that.host_addr; - sa.spi = st->st_esp.attrs.spi; - } - set_text_said(text_said, sa.dst, sa.spi, sa.proto); - - sa.text_said = text_said; - - DBG(DBG_KLIPS, - DBG_log("get %s", text_said) - ) - if (!kernel_ops->get_sa(&sa, bytes)) - return FALSE; - DBG(DBG_KLIPS, - DBG_log(" current: %d bytes", *bytes) - ) - - if (st->st_serialno == c->spd.eroute_owner) - { - DBG(DBG_KLIPS, - DBG_log("get %sbound policy with reqid %u" - , inbound? "in":"out", (u_int)c->spd.reqid + 1) - ) - sa.transport_proto = c->spd.this.protocol; - sa.encapsulation = st->st_esp.attrs.encapsulation; - - if (inbound) - { - sa.src_client = &c->spd.that.client; - sa.dst_client = &c->spd.this.client; - } - else - { - sa.src_client = &c->spd.this.client; - sa.dst_client = &c->spd.that.client; - } - if (!kernel_ops->get_policy(&sa, inbound, use_time)) - return FALSE; - DBG(DBG_KLIPS, - DBG_log(" use_time: %s", timetoa(use_time, FALSE)) - ) - } - return TRUE; -} - -const struct kernel_ops *kernel_ops; - -#endif /* KLIPS */ - -void -init_kernel(void) -{ -#ifdef KLIPS - - if (no_klips) - { - kernel_ops = &noklips_kernel_ops; - return; - } - - init_pfkey(); - - kernel_ops = &klips_kernel_ops; - -#if defined(linux) && defined(KERNEL26_SUPPORT) - { - bool linux_ipsec = 0; - struct stat buf; - - linux_ipsec = (stat("/proc/net/pfkey", &buf) == 0); - if (linux_ipsec) - { - plog("Using Linux 2.6 IPsec interface code"); - kernel_ops = &linux_kernel_ops; - } - else - { - plog("Using KLIPS IPsec interface code"); - } - } -#endif - - if (kernel_ops->init) - { - kernel_ops->init(); - } - - /* register SA types that we can negotiate */ - can_do_IPcomp = FALSE; /* until we get a response from KLIPS */ - kernel_ops->pfkey_register(); - - if (!kernel_ops->policy_lifetime) - { - event_schedule(EVENT_SHUNT_SCAN, SHUNT_SCAN_INTERVAL, NULL); - } -#endif -} - -/* 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) -{ - struct connection *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; - struct connection *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; - } - -#ifdef KLIPS - /* (attempt to) actually set up the SAs */ - return setup_half_ipsec_sa(st, TRUE); -#else /* !KLIPS */ - DBG(DBG_CONTROL, DBG_log("install_inbound_ipsec_sa()")); - return TRUE; -#endif /* !KLIPS */ -} - -/* 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(struct connection *c USED_BY_KLIPS - , struct spd_route *sr USED_BY_KLIPS - , struct state *st USED_BY_KLIPS) -{ -#ifdef KLIPS - struct spd_route *esr; - struct spd_route *rosr; - struct connection *ero /* who, if anyone, owns our eroute? */ - , *ro = route_owner(c, &rosr, &ero, &esr); - bool eroute_installed = FALSE - , firewall_notified = FALSE - , route_installed = FALSE; - - struct connection *ero_top; - struct bare_shunt **bspp; - - 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 - - bspp = (ero == NULL) - ? bare_shunt_ptr(&sr->this.client, &sr->that.client, sr->this.protocol) - : NULL; - - /* install the eroute */ - - passert(bspp == NULL || ero == NULL); /* only one non-NULL */ - - if (bspp != NULL || 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 - /* remember to free bspp iff we make it out of here alive */ - } - 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, "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, "prepare"); /* just in case; ignore failure */ - route_installed = do_command(c, sr, "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, "unroute"); - route_installed = do_command(c, sr, "route"); - } - else - { - route_installed = do_command(c, sr, "route"); - (void) do_command(ro, sr, "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 (bspp != NULL) - { - free_bare_shunt(bspp); - } - else if (ero != NULL && ero != c) - { - /* check if ero is an ancestor of c. */ - struct connection *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, "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 (bspp != NULL) - { - /* Restore old bare_shunt. - * I don't think that this case is very likely. - * Normally a bare shunt would have been assigned - * to a connection before we've gotten this far. - */ - struct bare_shunt *bs = *bspp; - - (void) raw_eroute(&bs->said.dst /* should be useless */ - , &bs->ours - , &bs->said.dst /* should be useless */ - , &bs->his - , bs->said.spi /* network order */ - , SA_INT - , SADB_X_SATYPE_INT - , 0 - , null_proto_info - , SHUNT_PATIENCE - , ERO_REPLACE, "restore"); - } - else 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; - } -#else /* !KLIPS */ - return TRUE; -#endif /* !KLIPS */ -} - -bool -install_ipsec_sa(struct state *st, bool inbound_also USED_BY_KLIPS) -{ -#ifdef KLIPS - 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; - } - } - } -#else /* !KLIPS */ - DBG(DBG_CONTROL, DBG_log("install_ipsec_sa() %s" - , inbound_also? "inbound and oubound" : "outbound only")); - - switch (could_route(st->st_connection)) - { - case route_easy: - case route_nearconflict: - break; - - default: - return FALSE; - } - - -#endif /* !KLIPS */ - - 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 USED_BY_KLIPS, bool inbound_only USED_BY_KLIPS) -{ -#ifdef KLIPS - if (!inbound_only) - { - /* If the state is the eroute owner, we must adjust - * the routing for the connection. - */ - struct connection *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, "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); -#else /* !KLIPS */ - DBG(DBG_CONTROL, DBG_log("if I knew how, I'd eroute() and teardown_ipsec_sa()")); -#endif /* !KLIPS */ -} -#ifdef NAT_TRAVERSAL -#ifdef KLIPS -static bool update_nat_t_ipsec_esp_sa (struct state *st, bool inbound) -{ - struct connection *c = st->st_connection; - char text_said[SATOT_BUF]; - struct kernel_sa sa; - ip_address - src = inbound? c->spd.that.host_addr : c->spd.this.host_addr, - dst = inbound? c->spd.this.host_addr : c->spd.that.host_addr; - - - ipsec_spi_t esp_spi = inbound? st->st_esp.our_spi : st->st_esp.attrs.spi; - - u_int16_t - natt_sport = inbound? c->spd.that.host_port : c->spd.this.host_port, - natt_dport = inbound? c->spd.this.host_port : c->spd.that.host_port; - - set_text_said(text_said, &dst, esp_spi, SA_ESP); - - memset(&sa, 0, sizeof(sa)); - sa.spi = esp_spi; - sa.src = &src; - sa.dst = &dst; - sa.text_said = text_said; - sa.authalg = alg_info_esp_aa2sadb(st->st_esp.attrs.auth); - sa.natt_sport = natt_sport; - sa.natt_dport = natt_dport; - sa.transid = st->st_esp.attrs.transid; - - return kernel_ops->add_sa(&sa, TRUE); - -} -#endif - -bool update_ipsec_sa (struct state *st USED_BY_KLIPS) -{ -#ifdef KLIPS - 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; -#else /* !KLIPS */ - DBG(DBG_CONTROL, DBG_log("if I knew how, I'd update_ipsec_sa()")); - return TRUE; -#endif /* !KLIPS */ -} -#endif - -/* 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) -{ - static const char procname[] = "/proc/net/ipsec_spi"; - FILE *f; - char buf[1024]; - u_int bytes; - int ret = TRUE; - - passert(st != NULL); - - f = fopen(procname, "r"); - if (f == NULL) - { - /* Can't open the file, perhaps were are on 26sec? */ - time_t use_time; - - if (get_sa_info(st, TRUE, &bytes, &use_time) - && use_time != UNDEFINED_TIME) - { - *idle_time = time(NULL) - use_time; - ret = *idle_time >= idle_max; - } - } - else - { - while (f != NULL) - { - char *line; - char text_said[SATOT_BUF]; - u_int8_t proto = 0; - ip_address dst; - ip_said said; - ipsec_spi_t spi = 0; - static const char idle[] = "idle="; - - dst = st->st_connection->spd.this.host_addr; /* inbound SA */ - if (st->st_ah.present) - { - proto = SA_AH; - spi = st->st_ah.our_spi; - } - if (st->st_esp.present) - { - proto = SA_ESP; - spi = st->st_esp.our_spi; - } - - if (proto == 0 && spi == 0) - { - ret = TRUE; - break; - } - - initsaid(&dst, spi, proto, &said); - satot(&said, 'x', text_said, SATOT_BUF); - - line = fgets(buf, sizeof(buf), f); - if (line == NULL) - { - /* Reached end of list */ - ret = TRUE; - break; - } - - if (strncmp(line, text_said, strlen(text_said)) == 0) - { - /* we found a match, now try to find idle= */ - char *p = strstr(line, idle); - - if (p == NULL) - { - /* SAs which haven't been used yet don't have it */ - ret = TRUE; /* it didn't have traffic */ - break; - } - p += sizeof(idle)-1; - if (*p == '\0') - { - ret = TRUE; /* be paranoid */ - break; - } - if (sscanf(p, "%d", (int *) idle_time) <= 0) - { - ret = TRUE; - break; - } - if (*idle_time >= idle_max) - { - ret = TRUE; - break; - } - else - { - ret = FALSE; - break; - } - } - } - fclose(f); - } - return ret; -} |