diff options
Diffstat (limited to 'src/pluto/connections.c')
-rw-r--r-- | src/pluto/connections.c | 4406 |
1 files changed, 4406 insertions, 0 deletions
diff --git a/src/pluto/connections.c b/src/pluto/connections.c new file mode 100644 index 000000000..0d02b979c --- /dev/null +++ b/src/pluto/connections.c @@ -0,0 +1,4406 @@ +/* information about connections between hosts and clients + * 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: connections.c,v 1.43 2006/04/29 18:16:02 as Exp $ + */ + +#include <string.h> +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <resolv.h> +#include <arpa/nameser.h> /* missing from <resolv.h> on old systems */ +#include <sys/queue.h> + +#include <freeswan.h> +#include <ipsec_policy.h> +#include "kameipsec.h" + +#include "constants.h" +#include "defs.h" +#include "id.h" +#include "x509.h" +#include "ca.h" +#include "crl.h" +#include "pgp.h" +#include "certs.h" +#include "ac.h" +#include "smartcard.h" +#include "fetch.h" +#include "connections.h" +#include "foodgroups.h" +#include "demux.h" +#include "state.h" +#include "timer.h" +#include "ipsec_doi.h" /* needs demux.h and state.h */ +#include "server.h" +#include "kernel.h" +#include "log.h" +#include "keys.h" +#include "adns.h" /* needs <resolv.h> */ +#include "dnskey.h" /* needs keys.h and adns.h */ +#include "whack.h" +#include "alg_info.h" +#include "ike_alg.h" +#include "nat_traversal.h" +#include "virtual.h" + +static void flush_pending_by_connection(struct connection *c); /* forward */ + +static struct connection *connections = NULL; + +/* struct host_pair: a nexus of information about a pair of hosts. + * A host is an IP address, UDP port pair. This is a debatable choice: + * - should port be considered (no choice of port in standard)? + * - should ID be considered (hard because not always known)? + * - should IP address matter on our end (we don't know our end)? + * Only oriented connections are registered. + * Unoriented connections are kept on the unoriented_connections + * linked list (using hp_next). For them, host_pair is NULL. + */ + +struct host_pair { + struct { + ip_address addr; + u_int16_t port; /* host order */ + } me, him; + bool initial_connection_sent; + struct connection *connections; /* connections with this pair */ + struct pending *pending; /* awaiting Keying Channel */ + struct host_pair *next; +}; + +static struct host_pair *host_pairs = NULL; + +static struct connection *unoriented_connections = NULL; + +/* check to see that Ids of peers match */ +bool +same_peer_ids(const struct connection *c, const struct connection *d +, const struct id *his_id) +{ + return same_id(&c->spd.this.id, &d->spd.this.id) + && same_id(his_id == NULL? &c->spd.that.id : his_id, &d->spd.that.id); +} + +static struct host_pair * +find_host_pair(const ip_address *myaddr, u_int16_t myport +, const ip_address *hisaddr, u_int16_t hisport) +{ + struct host_pair *p, *prev; + + /* default hisaddr to an appropriate any */ + if (hisaddr == NULL) + hisaddr = aftoinfo(addrtypeof(myaddr))->any; + + if (nat_traversal_enabled) + { + /** + * port is not relevant in host_pair. with nat_traversal we + * always use pluto_port (500) + */ + myport = pluto_port; + hisport = pluto_port; + } + + for (prev = NULL, p = host_pairs; p != NULL; prev = p, p = p->next) + { + if (sameaddr(&p->me.addr, myaddr) && p->me.port == myport + && sameaddr(&p->him.addr, hisaddr) && p->him.port == hisport) + { + if (prev != NULL) + { + prev->next = p->next; /* remove p from list */ + p->next = host_pairs; /* and stick it on front */ + host_pairs = p; + } + break; + } + } + return p; +} + +/* find head of list of connections with this pair of hosts */ +static struct connection * +find_host_pair_connections(const ip_address *myaddr, u_int16_t myport +, const ip_address *hisaddr, u_int16_t hisport) +{ + struct host_pair *hp = find_host_pair(myaddr, myport, hisaddr, hisport); + + if (nat_traversal_enabled && hp && hisaddr) + { + struct connection *c; + + for (c = hp->connections; c != NULL; c = c->hp_next) + { + if (c->spd.this.host_port == myport && c->spd.that.host_port == hisport) + return c; + } + return NULL; + } + return hp == NULL? NULL : hp->connections; +} + +static void +connect_to_host_pair(struct connection *c) +{ + if (oriented(*c)) + { + struct host_pair *hp = find_host_pair(&c->spd.this.host_addr, c->spd.this.host_port + , &c->spd.that.host_addr, c->spd.that.host_port); + + if (hp == NULL) + { + /* no suitable host_pair -- build one */ + hp = alloc_thing(struct host_pair, "host_pair"); + hp->me.addr = c->spd.this.host_addr; + hp->him.addr = c->spd.that.host_addr; + hp->me.port = nat_traversal_enabled ? pluto_port : c->spd.this.host_port; + hp->him.port = nat_traversal_enabled ? pluto_port : c->spd.that.host_port; + hp->initial_connection_sent = FALSE; + hp->connections = NULL; + hp->pending = NULL; + hp->next = host_pairs; + host_pairs = hp; + } + c->host_pair = hp; + c->hp_next = hp->connections; + hp->connections = c; + } + else + { + /* since this connection isn't oriented, we place it + * in the unoriented_connections list instead. + */ + c->host_pair = NULL; + c->hp_next = unoriented_connections; + unoriented_connections = c; + } +} + +/* find a connection by name. + * If strict, don't accept a CK_INSTANCE. + * Move the winner (if any) to the front. + * If none is found, and strict, a diagnostic is logged to whack. + */ +struct connection * +con_by_name(const char *nm, bool strict) +{ + struct connection *p, *prev; + + for (prev = NULL, p = connections; ; prev = p, p = p->ac_next) + { + if (p == NULL) + { + if (strict) + whack_log(RC_UNKNOWN_NAME + , "no connection named \"%s\"", nm); + break; + } + if (streq(p->name, nm) + && (!strict || p->kind != CK_INSTANCE)) + { + if (prev != NULL) + { + prev->ac_next = p->ac_next; /* remove p from list */ + p->ac_next = connections; /* and stick it on front */ + connections = p; + } + break; + } + } + return p; +} + +void +release_connection(struct connection *c, bool relations) +{ + if (c->kind == CK_INSTANCE) + { + /* This does everything we need. + * Note that we will be called recursively by delete_connection, + * but kind will be CK_GOING_AWAY. + */ + delete_connection(c, relations); + } + else + { + flush_pending_by_connection(c); + delete_states_by_connection(c, relations); + unroute_connection(c); + } +} + +/* Delete a connection */ + +#define list_rm(etype, enext, e, ehead) { \ + etype **ep; \ + for (ep = &(ehead); *ep != (e); ep = &(*ep)->enext) \ + passert(*ep != NULL); /* we must not come up empty-handed */ \ + *ep = (e)->enext; \ + } + + +void +delete_connection(struct connection *c, bool relations) +{ + struct connection *old_cur_connection + = cur_connection == c? NULL : cur_connection; +#ifdef DEBUG + lset_t old_cur_debugging = cur_debugging; +#endif + + set_cur_connection(c); + + /* Must be careful to avoid circularity: + * we mark c as going away so it won't get deleted recursively. + */ + passert(c->kind != CK_GOING_AWAY); + if (c->kind == CK_INSTANCE) + { + plog("deleting connection \"%s\" instance with peer %s {isakmp=#%lu/ipsec=#%lu}" + , c->name + , ip_str(&c->spd.that.host_addr) + , c->newest_isakmp_sa, c->newest_ipsec_sa); + c->kind = CK_GOING_AWAY; + } + else + { + plog("deleting connection"); + } + release_connection(c, relations); /* won't delete c */ + + if (c->kind == CK_GROUP) + delete_group(c); + + /* free up any logging resources */ + perpeer_logfree(c); + + /* find and delete c from connections list */ + list_rm(struct connection, ac_next, c, connections); + cur_connection = old_cur_connection; + + /* find and delete c from the host pair list */ + if (c->host_pair == NULL) + { + if (c->ikev1) + list_rm(struct connection, hp_next, c, unoriented_connections); + } + else + { + struct host_pair *hp = c->host_pair; + + list_rm(struct connection, hp_next, c, hp->connections); + c->host_pair = NULL; /* redundant, but safe */ + + /* if there are no more connections with this host_pair + * and we haven't even made an initial contact, let's delete + * this guy in case we were created by an attempted DOS attack. + */ + if (hp->connections == NULL + && !hp->initial_connection_sent) + { + passert(hp->pending == NULL); /* ??? must deal with this! */ + list_rm(struct host_pair, next, hp, host_pairs); + pfree(hp); + } + } + + if (c->kind != CK_GOING_AWAY) + pfreeany(c->spd.that.virt); + +#ifdef DEBUG + cur_debugging = old_cur_debugging; +#endif + pfreeany(c->name); + free_id_content(&c->spd.this.id); + pfreeany(c->spd.this.updown); + freeanychunk(c->spd.this.ca); + free_ietfAttrList(c->spd.this.groups); + free_id_content(&c->spd.that.id); + pfreeany(c->spd.that.updown); + freeanychunk(c->spd.that.ca); + free_ietfAttrList(c->spd.that.groups); + free_generalNames(c->requested_ca, TRUE); + gw_delref(&c->gw_info); + + lock_certs_and_keys("delete_connection"); + release_cert(c->spd.this.cert); + scx_release(c->spd.this.sc); + release_cert(c->spd.that.cert); + scx_release(c->spd.that.sc); + unlock_certs_and_keys("delete_connection"); + + alg_info_delref((struct alg_info **)&c->alg_info_esp); + alg_info_delref((struct alg_info **)&c->alg_info_ike); + + pfree(c); +} + +/* Delete connections with the specified name */ +void +delete_connections_by_name(const char *name, bool strict) +{ + struct connection *c = con_by_name(name, strict); + + for (; c != NULL; c = con_by_name(name, FALSE)) + delete_connection(c, FALSE); +} + +void +delete_every_connection(void) +{ + while (connections != NULL) + delete_connection(connections, TRUE); +} + +void +release_dead_interfaces(void) +{ + struct host_pair *hp; + + for (hp = host_pairs; hp != NULL; hp = hp->next) + { + struct connection **pp + , *p; + + for (pp = &hp->connections; (p = *pp) != NULL; ) + { + if (p->interface->change == IFN_DELETE) + { + /* this connection's interface is going away */ + enum connection_kind k = p->kind; + + release_connection(p, TRUE); + + if (k <= CK_PERMANENT) + { + /* The connection should have survived release: + * move it to the unoriented_connections list. + */ + passert(p == *pp); + + p->interface = NULL; + + *pp = p->hp_next; /* advance *pp */ + p->host_pair = NULL; + p->hp_next = unoriented_connections; + unoriented_connections = p; + } + else + { + /* The connection should have vanished, + * but the previous connection remains. + */ + passert(p != *pp); + } + } + else + { + pp = &p->hp_next; /* advance pp */ + } + } + } +} + +/* adjust orientations of connections to reflect newly added interfaces */ +void +check_orientations(void) +{ + /* try to orient all the unoriented connections */ + { + struct connection *c = unoriented_connections; + + unoriented_connections = NULL; + + while (c != NULL) + { + struct connection *nxt = c->hp_next; + + (void)orient(c); + connect_to_host_pair(c); + c = nxt; + } + } + + /* Check that no oriented connection has become double-oriented. + * In other words, the far side must not match one of our new interfaces. + */ + { + struct iface *i; + + for (i = interfaces; i != NULL; i = i->next) + { + if (i->change == IFN_ADD) + { + struct host_pair *hp; + + for (hp = host_pairs; hp != NULL; hp = hp->next) + { + if (sameaddr(&hp->him.addr, &i->addr) + && (!no_klips || hp->him.port == pluto_port)) + { + /* bad news: the whole chain of connections + * hanging off this host pair has both sides + * matching an interface. + * We'll get rid of them, using orient and + * connect_to_host_pair. But we'll be lazy + * and not ditch the host_pair itself (the + * cost of leaving it is slight and cannot + * be induced by a foe). + */ + struct connection *c = hp->connections; + + hp->connections = NULL; + while (c != NULL) + { + struct connection *nxt = c->hp_next; + + c->interface = NULL; + (void)orient(c); + connect_to_host_pair(c); + c = nxt; + } + } + } + } + } + } +} + +static err_t +default_end(struct end *e, ip_address *dflt_nexthop) +{ + err_t ugh = NULL; + const struct af_info *afi = aftoinfo(addrtypeof(&e->host_addr)); + + if (afi == NULL) + return "unknown address family in default_end"; + + /* default ID to IP (but only if not NO_IP -- WildCard) */ + if (e->id.kind == ID_NONE && !isanyaddr(&e->host_addr)) + { + e->id.kind = afi->id_addr; + e->id.ip_addr = e->host_addr; + e->has_id_wildcards = FALSE; + } + + /* default nexthop to other side */ + if (isanyaddr(&e->host_nexthop)) + e->host_nexthop = *dflt_nexthop; + + /* default client to subnet containing only self + * XXX This may mean that the client's address family doesn't match + * tunnel_addr_family. + */ + if (!e->has_client) + ugh = addrtosubnet(&e->host_addr, &e->client); + + return ugh; +} + +/* Format the topology of a connection end, leaving out defaults. + * Largest left end looks like: client === host : port [ host_id ] --- hop + * Note: if that==NULL, skip nexthop + * Returns strlen of formated result (length excludes NUL at end). + */ +size_t +format_end(char *buf +, size_t buf_len +, const struct end *this +, const struct end *that +, bool is_left +, lset_t policy) +{ + char client[SUBNETTOT_BUF]; + const char *client_sep = ""; + char protoport[sizeof(":255/65535")]; + const char *host = NULL; + char host_space[ADDRTOT_BUF]; + char host_port[sizeof(":65535")]; + char host_id[BUF_LEN + 2]; + char hop[ADDRTOT_BUF]; + const char *hop_sep = ""; + const char *open_brackets = ""; + const char *close_brackets = ""; + + if (isanyaddr(&this->host_addr)) + { + switch (policy & (POLICY_GROUP | POLICY_OPPO)) + { + case POLICY_GROUP: + host = "%group"; + break; + case POLICY_OPPO: + host = "%opportunistic"; + break; + case POLICY_GROUP | POLICY_OPPO: + host = "%opportunisticgroup"; + break; + default: + host = "%any"; + break; + } + } + + client[0] = '\0'; + + if (is_virtual_end(this) && isanyaddr(&this->host_addr)) + { + host = "%virtual"; + } + + /* [client===] */ + if (this->has_client) + { + ip_address client_net, client_mask; + + networkof(&this->client, &client_net); + maskof(&this->client, &client_mask); + client_sep = "==="; + + /* {client_subnet_wildcard} */ + if (this->has_client_wildcard) + { + open_brackets = "{"; + close_brackets = "}"; + } + + if (isanyaddr(&client_net) && isanyaddr(&client_mask) + && (policy & (POLICY_GROUP | POLICY_OPPO))) + client_sep = ""; /* boring case */ + else if (subnetisnone(&this->client)) + strcpy(client, "?"); + else + subnettot(&this->client, 0, client, sizeof(client)); + } + else if (this->modecfg && isanyaddr(&this->host_srcip)) + { + /* we are mode config client */ + client_sep = "==="; + strcpy(client, "%modecfg"); + } + + /* host */ + if (host == NULL) + { + addrtot(&this->host_addr, 0, host_space, sizeof(host_space)); + host = host_space; + } + + host_port[0] = '\0'; + if (this->host_port != IKE_UDP_PORT) + snprintf(host_port, sizeof(host_port), ":%u" + , this->host_port); + + /* payload portocol and port */ + protoport[0] = '\0'; + if (this->has_port_wildcard) + snprintf(protoport, sizeof(protoport), ":%u/%%any", this->protocol); + else if (this->port || this->protocol) + snprintf(protoport, sizeof(protoport), ":%u/%u", this->protocol + , this->port); + + /* id, if different from host */ + host_id[0] = '\0'; + if (this->id.kind == ID_MYID) + { + strcpy(host_id, "[%myid]"); + } + else if (!(this->id.kind == ID_NONE + || (id_is_ipaddr(&this->id) && sameaddr(&this->id.ip_addr, &this->host_addr)))) + { + int len = idtoa(&this->id, host_id+1, sizeof(host_id)-2); + + host_id[0] = '['; + strcpy(&host_id[len < 0? (ptrdiff_t)sizeof(host_id)-2 : 1 + len], "]"); + } + + /* [---hop] */ + hop[0] = '\0'; + hop_sep = ""; + if (that != NULL && !sameaddr(&this->host_nexthop, &that->host_addr)) + { + addrtot(&this->host_nexthop, 0, hop, sizeof(hop)); + hop_sep = "---"; + } + + if (is_left) + snprintf(buf, buf_len, "%s%s%s%s%s%s%s%s%s%s" + , open_brackets, client, close_brackets + , client_sep, host, host_port, host_id + , protoport, hop_sep, hop); + else + snprintf(buf, buf_len, "%s%s%s%s%s%s%s%s%s%s" + , hop, hop_sep, host, host_port, host_id + , protoport, client_sep + , open_brackets, client, close_brackets); + return strlen(buf); +} + +/* format topology of a connection. + * Two symmetric ends separated by ... + */ +#define CONNECTION_BUF (2 * (END_BUF - 1) + 4) + +static size_t +format_connection(char *buf, size_t buf_len + , const struct connection *c + , struct spd_route *sr) +{ + size_t w = format_end(buf, buf_len, &sr->this, &sr->that, TRUE, LEMPTY); + + w += snprintf(buf + w, buf_len - w, "..."); + return w + format_end(buf + w, buf_len - w, &sr->that, &sr->this, FALSE, c->policy); +} + +static void +unshare_connection_strings(struct connection *c) +{ + c->name = clone_str(c->name, "connection name"); + + unshare_id_content(&c->spd.this.id); + c->spd.this.updown = clone_str(c->spd.this.updown, "updown"); + scx_share(c->spd.this.sc); + share_cert(c->spd.this.cert); + if (c->spd.this.ca.ptr != NULL) + clonetochunk(c->spd.this.ca, c->spd.this.ca.ptr, c->spd.this.ca.len, "ca string"); + + unshare_id_content(&c->spd.that.id); + c->spd.that.updown = clone_str(c->spd.that.updown, "updown"); + scx_share(c->spd.that.sc); + share_cert(c->spd.that.cert); + if (c->spd.that.ca.ptr != NULL) + clonetochunk(c->spd.that.ca, c->spd.that.ca.ptr, c->spd.that.ca.len, "ca string"); + + /* increment references to algo's */ + alg_info_addref((struct alg_info *)c->alg_info_esp); + alg_info_addref((struct alg_info *)c->alg_info_ike); +} + +static void +load_end_certificate(const char *filename, struct end *dst) +{ + time_t valid_until; + cert_t cert; + bool valid_cert = FALSE; + bool cached_cert = FALSE; + + /* initialize end certificate */ + dst->cert.type = CERT_NONE; + dst->cert.u.x509 = NULL; + + /* initialize smartcard info record */ + dst->sc = NULL; + + if (filename != NULL) + { + if (scx_on_smartcard(filename)) + { + /* load cert from smartcard */ + valid_cert = scx_load_cert(filename, &dst->sc, &cert, &cached_cert); + } + else + { + /* load cert from file */ + valid_cert = load_host_cert(filename, &cert); + } + } + + if (valid_cert) + { + err_t ugh = NULL; + + switch (cert.type) + { + case CERT_PGP: + select_pgpcert_id(cert.u.pgp, &dst->id); + + if (cached_cert) + dst->cert = cert; + else + { + valid_until = cert.u.pgp->until; + add_pgp_public_key(cert.u.pgp, cert.u.pgp->until, DAL_LOCAL); + dst->cert.type = cert.type; + dst->cert.u.pgp = add_pgpcert(cert.u.pgp); + } + break; + case CERT_X509_SIGNATURE: + select_x509cert_id(cert.u.x509, &dst->id); + + if (cached_cert) + dst->cert = cert; + else + { + /* check validity of cert */ + valid_until = cert.u.x509->notAfter; + ugh = check_validity(cert.u.x509, &valid_until); + if (ugh != NULL) + { + plog(" %s", ugh); + free_x509cert(cert.u.x509); + break; + } + + DBG(DBG_CONTROL, + DBG_log("certificate is valid") + ) + add_x509_public_key(cert.u.x509, valid_until, DAL_LOCAL); + dst->cert.type = cert.type; + dst->cert.u.x509 = add_x509cert(cert.u.x509); + } + /* if no CA is defined, use issuer as default */ + if (dst->ca.ptr == NULL) + dst->ca = dst->cert.u.x509->issuer; + break; + default: + break; + } + + /* cache the certificate that was last retrieved from the smartcard */ + if (dst->sc != NULL) + { + if (!same_cert(&dst->sc->last_cert, &dst->cert)) + { + lock_certs_and_keys("load_end_certificates"); + release_cert(dst->sc->last_cert); + dst->sc->last_cert = dst->cert; + share_cert(dst->cert); + unlock_certs_and_keys("load_end_certificates"); + } + time(&dst->sc->last_load); + } + } +} + +static bool +extract_end(struct end *dst, const whack_end_t *src, const char *which) +{ + bool same_ca = FALSE; + + /* decode id, if any */ + if (src->id == NULL) + { + dst->id.kind = ID_NONE; + } + else + { + err_t ugh = atoid(src->id, &dst->id, TRUE); + + if (ugh != NULL) + { + loglog(RC_BADID, "bad %s --id: %s (ignored)", which, ugh); + dst->id = empty_id; /* ignore bad one */ + } + } + + dst->ca = empty_chunk; + + /* decode CA distinguished name, if any */ + if (src->ca != NULL) + { + if streq(src->ca, "%same") + same_ca = TRUE; + else if (!streq(src->ca, "%any")) + { + err_t ugh; + + dst->ca.ptr = temporary_cyclic_buffer(); + ugh = atodn(src->ca, &dst->ca); + if (ugh != NULL) + { + plog("bad CA string '%s': %s (ignored)", src->ca, ugh); + dst->ca = empty_chunk; + } + } + } + + /* load local end certificate and extract ID, if any */ + load_end_certificate(src->cert, dst); + + /* does id has wildcards? */ + dst->has_id_wildcards = id_count_wildcards(&dst->id) > 0; + + /* decode group attributes, if any */ + decode_groups(src->groups, &dst->groups); + + /* the rest is simple copying of corresponding fields */ + dst->host_addr = src->host_addr; + dst->host_nexthop = src->host_nexthop; + dst->host_srcip = src->host_srcip; + dst->has_natip = src->has_natip; + dst->client = src->client; + dst->protocol = src->protocol; + dst->port = src->port; + dst->has_port_wildcard = src->has_port_wildcard; + dst->key_from_DNS_on_demand = src->key_from_DNS_on_demand; + dst->has_client = src->has_client; + dst->has_client_wildcard = src->has_client_wildcard; + dst->modecfg = src->modecfg; + dst->hostaccess = src->hostaccess; + dst->sendcert = src->sendcert; + dst->updown = src->updown; + dst->host_port = src->host_port; + + /* if host sourceip is defined but no client is present + * behind the host then set client to sourceip/32 + */ + if (addrbytesptr(&dst->host_srcip, NULL) + && !isanyaddr(&dst->host_srcip) + && !dst->has_natip + && !dst->has_client) + { + err_t ugh = addrtosubnet(&dst->host_srcip, &dst->client); + + if (ugh != NULL) + plog("could not assign host sourceip to client subnet"); + else + dst->has_client = TRUE; + } + return same_ca; +} + +static bool +check_connection_end(const whack_end_t *this, const whack_end_t *that +, const whack_message_t *wm) +{ + if (wm->addr_family != addrtypeof(&this->host_addr) + || wm->addr_family != addrtypeof(&this->host_nexthop) + || (this->has_client? wm->tunnel_addr_family : wm->addr_family) + != subnettypeof(&this->client) + || subnettypeof(&this->client) != subnettypeof(&that->client)) + { + /* this should have been diagnosed by whack, so we need not be clear + * !!! overloaded use of RC_CLASH + */ + loglog(RC_CLASH, "address family inconsistency in connection"); + return FALSE; + } + + if (isanyaddr(&that->host_addr)) + { + /* other side is wildcard: we must check if other conditions met */ + if (isanyaddr(&this->host_addr)) + { + loglog(RC_ORIENT, "connection must specify host IP address for our side"); + return FALSE; + } + } + + if (this->virt && (!isanyaddr(&this->host_addr) || this->has_client)) + { + loglog(RC_CLASH, + "virtual IP must only be used with %%any and without client"); + return FALSE; + } + + return TRUE; /* happy */ +} + +struct connection * +find_connection_by_reqid(uint32_t reqid) +{ + struct connection *c; + + reqid &= ~3; + for (c = connections; c != NULL; c = c->ac_next) + { + if (c->spd.reqid == reqid) + return c; + } + + return NULL; +} + +static uint32_t +gen_reqid(void) +{ + uint32_t start; + static uint32_t reqid = IPSEC_MANUAL_REQID_MAX & ~3; + + start = reqid; + do { + reqid += 4; + if (reqid == 0) + reqid = (IPSEC_MANUAL_REQID_MAX & ~3) + 4; + if (!find_connection_by_reqid(reqid)) + return reqid; + } while (reqid != start); + + exit_log("unable to allocate reqid"); +} + +void +add_connection(const whack_message_t *wm) +{ + if (con_by_name(wm->name, FALSE) != NULL) + { + loglog(RC_DUPNAME, "attempt to redefine connection \"%s\"", wm->name); + } + else if (wm->right.protocol != wm->left.protocol) + { + /* this should haven been diagnosed by whack + * !!! overloaded use of RC_CLASH + */ + loglog(RC_CLASH, "the protocol must be the same for leftport and rightport"); + } + else if (check_connection_end(&wm->right, &wm->left, wm) + && check_connection_end(&wm->left, &wm->right, wm)) + { + bool same_rightca, same_leftca; + struct connection *c = alloc_thing(struct connection, "struct connection"); + + c->name = wm->name; + c->ikev1 = wm->ikev1; + c->policy = wm->policy; + + if ((c->policy & POLICY_COMPRESS) && !can_do_IPcomp) + loglog(RC_COMMENT + , "ignoring --compress in \"%s\" because KLIPS is not configured to do IPCOMP" + , c->name); + + if (wm->esp) + { + const char *ugh; + + DBG(DBG_CONTROL, + DBG_log("from whack: got --esp=%s", wm->esp ? wm->esp: "NULL") + ) + c->alg_info_esp= alg_info_esp_create_from_str(wm->esp? wm->esp : "", &ugh); + + DBG(DBG_CRYPT|DBG_CONTROL, + static char buf[256]="<NULL>"; + + if (c->alg_info_esp) + alg_info_snprint(buf, sizeof(buf) + ,(struct alg_info *)c->alg_info_esp); + DBG_log("esp string values: %s", buf); + ) + if (c->alg_info_esp) + { + if (c->alg_info_esp->alg_info_cnt==0) + loglog(RC_LOG_SERIOUS + , "got 0 transforms for esp=\"%s\"", wm->esp); + } + else + { + loglog(RC_LOG_SERIOUS + , "esp string error: %s", ugh? ugh : "Unknown"); + } + } + + if (wm->ike) + { + const char *ugh; + + DBG(DBG_CONTROL, + DBG_log("from whack: got --ike=%s", wm->ike ? wm->ike: "NULL") + ) + c->alg_info_ike= alg_info_ike_create_from_str(wm->ike? wm->ike : "", &ugh); + + DBG(DBG_CRYPT|DBG_CONTROL, + static char buf[256]="<NULL>"; + + if (c->alg_info_ike) + alg_info_snprint(buf, sizeof(buf) + , (struct alg_info *)c->alg_info_ike); + DBG_log("ike string values: %s", buf); + ) + if (c->alg_info_ike) + { + if (c->alg_info_ike->alg_info_cnt==0) + loglog(RC_LOG_SERIOUS + , "got 0 transforms for ike=\"%s\"", wm->ike); + } + else + { + loglog(RC_LOG_SERIOUS + , "ike string error: %s", ugh? ugh : "Unknown"); + } + } + + c->sa_ike_life_seconds = wm->sa_ike_life_seconds; + c->sa_ipsec_life_seconds = wm->sa_ipsec_life_seconds; + c->sa_rekey_margin = wm->sa_rekey_margin; + c->sa_rekey_fuzz = wm->sa_rekey_fuzz; + c->sa_keying_tries = wm->sa_keying_tries; + + /* RFC 3706 DPD */ + c->dpd_delay = wm->dpd_delay; + c->dpd_timeout = wm->dpd_timeout; + c->dpd_action = wm->dpd_action; + + c->addr_family = wm->addr_family; + c->tunnel_addr_family = wm->tunnel_addr_family; + + c->requested_ca = NULL; + + same_leftca = extract_end(&c->spd.this, &wm->left, "left"); + same_rightca = extract_end(&c->spd.that, &wm->right, "right"); + + if (same_rightca) + c->spd.that.ca = c->spd.this.ca; + else if (same_leftca) + c->spd.this.ca = c->spd.that.ca; + + default_end(&c->spd.this, &c->spd.that.host_addr); + default_end(&c->spd.that, &c->spd.this.host_addr); + + /* force any wildcard host IP address, any wildcard subnet + * or any wildcard ID to that end + */ + if (isanyaddr(&c->spd.this.host_addr) || c->spd.this.has_client_wildcard + || c->spd.this.has_port_wildcard || c->spd.this.has_id_wildcards) + { + struct end t = c->spd.this; + + c->spd.this = c->spd.that; + c->spd.that = t; + } + + c->spd.next = NULL; + c->spd.reqid = gen_reqid(); + + /* set internal fields */ + c->instance_serial = 0; + c->ac_next = connections; + connections = c; + c->interface = NULL; + c->spd.routing = RT_UNROUTED; + c->newest_isakmp_sa = SOS_NOBODY; + c->newest_ipsec_sa = SOS_NOBODY; + c->spd.eroute_owner = SOS_NOBODY; + + if (c->policy & POLICY_GROUP) + { + c->kind = CK_GROUP; + add_group(c); + } + else if ((isanyaddr(&c->spd.that.host_addr) && !NEVER_NEGOTIATE(c->policy)) + || c->spd.that.has_client_wildcard || c->spd.that.has_port_wildcard + || c->spd.that.has_id_wildcards) + { + /* Opportunistic or Road Warrior or wildcard client subnet + * or wildcard ID */ + c->kind = CK_TEMPLATE; + } + else + { + c->kind = CK_PERMANENT; + } + set_policy_prio(c); /* must be after kind is set */ + +#ifdef DEBUG + c->extra_debugging = wm->debugging; +#endif + + c->gw_info = NULL; + + passert(!(wm->left.virt && wm->right.virt)); + if (wm->left.virt || wm->right.virt) + { + passert(isanyaddr(&c->spd.that.host_addr)); + c->spd.that.virt = create_virtual(c, + wm->left.virt ? wm->left.virt : wm->right.virt); + if (c->spd.that.virt) + c->spd.that.has_client = TRUE; + } + + unshare_connection_strings(c); + (void)orient(c); + + if (c->ikev1) + connect_to_host_pair(c); + + /* log all about this connection */ + plog("added connection description \"%s\"", c->name); + DBG(DBG_CONTROL, + char topo[CONNECTION_BUF]; + + (void) format_connection(topo, sizeof(topo), c, &c->spd); + + DBG_log("%s", topo); + + /* Make sure that address families can be correctly inferred + * from printed ends. + */ + passert(c->addr_family == addrtypeof(&c->spd.this.host_addr) + && c->addr_family == addrtypeof(&c->spd.this.host_nexthop) + && (c->spd.this.has_client? c->tunnel_addr_family : c->addr_family) + == subnettypeof(&c->spd.this.client) + + && c->addr_family == addrtypeof(&c->spd.that.host_addr) + && c->addr_family == addrtypeof(&c->spd.that.host_nexthop) + && (c->spd.that.has_client? c->tunnel_addr_family : c->addr_family) + == subnettypeof(&c->spd.that.client)); + + DBG_log("ike_life: %lus; ipsec_life: %lus; rekey_margin: %lus;" + " rekey_fuzz: %lu%%; keyingtries: %lu; policy: %s" + , (unsigned long) c->sa_ike_life_seconds + , (unsigned long) c->sa_ipsec_life_seconds + , (unsigned long) c->sa_rekey_margin + , (unsigned long) c->sa_rekey_fuzz + , (unsigned long) c->sa_keying_tries + , prettypolicy(c->policy)); + ); + } +} + +/* Derive a template connection from a group connection and target. + * Similar to instantiate(). Happens at whack --listen. + * Returns name of new connection. May be NULL. + * Caller is responsible for pfreeing. + */ +char * +add_group_instance(struct connection *group, const ip_subnet *target) +{ + char namebuf[100] + , targetbuf[SUBNETTOT_BUF]; + struct connection *t; + char *name = NULL; + + passert(group->kind == CK_GROUP); + passert(oriented(*group)); + + /* manufacture a unique name for this template */ + subnettot(target, 0, targetbuf, sizeof(targetbuf)); + snprintf(namebuf, sizeof(namebuf), "%s#%s", group->name, targetbuf); + + if (con_by_name(namebuf, FALSE) != NULL) + { + loglog(RC_DUPNAME, "group name + target yields duplicate name \"%s\"" + , namebuf); + } + else + { + t = clone_thing(*group, "group instance"); + t->name = namebuf; + unshare_connection_strings(t); + name = clone_str(t->name, "group instance name"); + t->spd.that.client = *target; + t->policy &= ~(POLICY_GROUP | POLICY_GROUTED); + t->kind = isanyaddr(&t->spd.that.host_addr) && !NEVER_NEGOTIATE(t->policy) + ? CK_TEMPLATE : CK_INSTANCE; + + /* reset log file info */ + t->log_file_name = NULL; + t->log_file = NULL; + t->log_file_err = FALSE; + + t->spd.reqid = gen_reqid(); + + if (t->spd.that.virt) + { + DBG_log("virtual_ip not supported in group instance"); + t->spd.that.virt = NULL; + } + + /* add to connections list */ + t->ac_next = connections; + connections = t; + + /* same host_pair as parent: stick after parent on list */ + group->hp_next = t; + + /* route if group is routed */ + if (group->policy & POLICY_GROUTED) + { + if (!trap_connection(t)) + whack_log(RC_ROUTE, "could not route"); + } + } + return name; +} + +/* an old target has disappeared for a group: delete instance */ +void +remove_group_instance(const struct connection *group USED_BY_DEBUG +, const char *name) +{ + passert(group->kind == CK_GROUP); + passert(oriented(*group)); + + delete_connections_by_name(name, FALSE); +} + +/* Common part of instantiating a Road Warrior or Opportunistic connection. + * his_id can be used to carry over an ID discovered in Phase 1. + * It must not disagree with the one in c, but if that is unspecified, + * the new connection will use his_id. + * If his_id is NULL, and c.that.id is uninstantiated (ID_NONE), the + * new connection will continue to have an uninstantiated that.id. + * Note: instantiation does not affect port numbers. + * + * Note that instantiate can only deal with a single SPD/eroute. + */ +static struct connection * +instantiate(struct connection *c, const ip_address *him +, u_int16_t his_port +, const struct id *his_id) +{ + struct connection *d; + int wildcards; + + passert(c->kind == CK_TEMPLATE); + passert(c->spd.next == NULL); + + c->instance_serial++; + d = clone_thing(*c, "temporary connection"); + if (his_id != NULL) + { + passert(match_id(his_id, &d->spd.that.id, &wildcards)); + d->spd.that.id = *his_id; + d->spd.that.has_id_wildcards = FALSE; + } + unshare_connection_strings(d); + unshare_ietfAttrList(&d->spd.this.groups); + unshare_ietfAttrList(&d->spd.that.groups); + d->kind = CK_INSTANCE; + + passert(oriented(*d)); + d->spd.that.host_addr = *him; + setportof(htons(c->spd.that.port), &d->spd.that.host_addr); + + if (his_port) d->spd.that.host_port = his_port; + + default_end(&d->spd.that, &d->spd.this.host_addr); + + /* We cannot guess what our next_hop should be, but if it was + * explicitly specified as 0.0.0.0, we set it to be him. + * (whack will not allow nexthop to be elided in RW case.) + */ + default_end(&d->spd.this, &d->spd.that.host_addr); + d->spd.next = NULL; + d->spd.reqid = gen_reqid(); + + /* set internal fields */ + d->ac_next = connections; + connections = d; + d->spd.routing = RT_UNROUTED; + d->newest_isakmp_sa = SOS_NOBODY; + d->newest_ipsec_sa = SOS_NOBODY; + d->spd.eroute_owner = SOS_NOBODY; + + /* reset log file info */ + d->log_file_name = NULL; + d->log_file = NULL; + d->log_file_err = FALSE; + + connect_to_host_pair(d); + + return d; +} + +struct connection * +rw_instantiate(struct connection *c, const ip_address *him, u_int16_t his_port +, const ip_subnet *his_net, const struct id *his_id) +{ + struct connection *d = instantiate(c, him, his_port, his_id); + + if (d && his_net && is_virtual_connection(c)) + { + d->spd.that.client = *his_net; + d->spd.that.virt = NULL; + if (subnetishost(his_net) && addrinsubnet(him, his_net)) + d->spd.that.has_client = FALSE; + } + + if (d->policy & POLICY_OPPO) + { + /* This must be before we know the client addresses. + * Fill in one that is impossible. This prevents anyone else from + * trying to use this connection to get to a particular client + */ + d->spd.that.client = *aftoinfo(subnettypeof(&d->spd.that.client))->none; + } + DBG(DBG_CONTROL + , DBG_log("instantiated \"%s\" for %s" , d->name, ip_str(him))); + return d; +} + +struct connection * +oppo_instantiate(struct connection *c +, const ip_address *him +, const struct id *his_id +, struct gw_info *gw +, const ip_address *our_client USED_BY_DEBUG +, const ip_address *peer_client) +{ + struct connection *d = instantiate(c, him, 0, his_id); + + passert(d->spd.next == NULL); + + /* fill in our client side */ + if (d->spd.this.has_client) + { + /* there was a client in the abstract connection + * so we demand that the required client is within that subnet. + */ + passert(addrinsubnet(our_client, &d->spd.this.client)); + happy(addrtosubnet(our_client, &d->spd.this.client)); + /* opportunistic connections do not use port selectors */ + setportof(0, &d->spd.this.client.addr); + } + else + { + /* there was no client in the abstract connection + * so we demand that the required client be the host + */ + passert(sameaddr(our_client, &d->spd.this.host_addr)); + } + + /* fill in peer's client side. + * If the client is the peer, excise the client from the connection. + */ + passert((d->policy & POLICY_OPPO) + && addrinsubnet(peer_client, &d->spd.that.client)); + happy(addrtosubnet(peer_client, &d->spd.that.client)); + /* opportunistic connections do not use port selectors */ + setportof(0, &d->spd.that.client.addr); + + if (sameaddr(peer_client, &d->spd.that.host_addr)) + d->spd.that.has_client = FALSE; + + passert(d->gw_info == NULL); + gw_addref(gw); + d->gw_info = gw; + + /* Adjust routing if something is eclipsing c. + * It must be a %hold for us (hard to passert this). + * If there was another instance eclipsing, we'd be using it. + */ + if (c->spd.routing == RT_ROUTED_ECLIPSED) + d->spd.routing = RT_ROUTED_PROSPECTIVE; + + /* Remember if the template is routed: + * if so, this instance applies for initiation + * even if it is created for responding. + */ + if (routed(c->spd.routing)) + d->instance_initiation_ok = TRUE; + + DBG(DBG_CONTROL, + char topo[CONNECTION_BUF]; + + (void) format_connection(topo, sizeof(topo), d, &d->spd); + DBG_log("instantiated \"%s\": %s", d->name, topo); + ); + return d; +} + +/* priority formatting */ +void +fmt_policy_prio(policy_prio_t pp, char buf[POLICY_PRIO_BUF]) +{ + if (pp == BOTTOM_PRIO) + snprintf(buf, POLICY_PRIO_BUF, "0"); + else + snprintf(buf, POLICY_PRIO_BUF, "%lu,%lu" + , pp>>16, (pp & ~(~(policy_prio_t)0 << 16)) >> 8); +} + +/* Format any information needed to identify an instance of a connection. + * Fills any needed information into buf which MUST be big enough. + * Road Warrior: peer's IP address + * Opportunistic: [" " myclient "==="] " ..." peer ["===" hisclient] '\0' + */ +static size_t +fmt_client(const ip_subnet *client, const ip_address *gw, const char *prefix, char buf[ADDRTOT_BUF]) +{ + if (subnetisaddr(client, gw)) + { + buf[0] = '\0'; /* compact denotation for "self" */ + } + else + { + char *ap; + + strcpy(buf, prefix); + ap = buf + strlen(prefix); + if (subnetisnone(client)) + strcpy(ap, "?"); /* unknown */ + else + subnettot(client, 0, ap, SUBNETTOT_BUF); + } + return strlen(buf); +} + +void +fmt_conn_instance(const struct connection *c, char buf[CONN_INST_BUF]) +{ + char *p = buf; + + *p = '\0'; + + if (c->kind == CK_INSTANCE) + { + if (c->instance_serial != 0) + { + snprintf(p, CONN_INST_BUF, "[%lu]", c->instance_serial); + p += strlen(p); + } + + if (c->policy & POLICY_OPPO) + { + size_t w = fmt_client(&c->spd.this.client, &c->spd.this.host_addr, " ", p); + + p += w; + + strcpy(p, w == 0? " ..." : "=== ..."); + p += strlen(p); + + addrtot(&c->spd.that.host_addr, 0, p, ADDRTOT_BUF); + p += strlen(p); + + (void) fmt_client(&c->spd.that.client, &c->spd.that.host_addr, "===", p); + } + else + { + *p++ = ' '; + addrtot(&c->spd.that.host_addr, 0, p, ADDRTOT_BUF); +# + if (c->spd.that.host_port != pluto_port) + { + p += strlen(p); + sprintf(p, ":%d", c->spd.that.host_port); + } + } + } +} + +/* Find an existing connection for a trapped outbound packet. + * This is attempted before we bother with gateway discovery. + * + this connection is routed or instance_of_routed_template + * (i.e. approved for on-demand) + * + this subnet contains our_client (or we are our_client) + * + that subnet contains peer_client (or peer is peer_client) + * + don't care about Phase 1 IDs (we don't know) + * Note: result may still need to be instantiated. + * The winner has the highest policy priority. + * + * If there are several with that priority, we give preference to + * the first one that is an instance. + * + * See also build_outgoing_opportunistic_connection. + */ +struct connection * +find_connection_for_clients(struct spd_route **srp, + const ip_address *our_client, + const ip_address *peer_client, + int transport_proto) +{ + struct connection *c = connections, *best = NULL; + policy_prio_t best_prio = BOTTOM_PRIO; + struct spd_route *sr; + struct spd_route *best_sr = NULL; + int our_port = ntohs(portof(our_client)); + int peer_port = ntohs(portof(peer_client)); + + passert(!isanyaddr(our_client) && !isanyaddr(peer_client)); +#ifdef DEBUG + if (DBGP(DBG_CONTROL)) + { + char ocb[ADDRTOT_BUF], pcb[ADDRTOT_BUF]; + + addrtot(our_client, 0, ocb, sizeof(ocb)); + addrtot(peer_client, 0, pcb, sizeof(pcb)); + DBG_log("find_connection: " + "looking for policy for connection: %s:%d/%d -> %s:%d/%d" + , ocb, transport_proto, our_port, pcb, transport_proto, peer_port); + } +#endif /* DEBUG */ + + for (c = connections; c != NULL; c = c->ac_next) + { + if (c->kind == CK_GROUP) + continue; + + for (sr = &c->spd; best!=c && sr; sr = sr->next) + { + if ((routed(sr->routing) || c->instance_initiation_ok) + && addrinsubnet(our_client, &sr->this.client) + && addrinsubnet(peer_client, &sr->that.client) + && addrinsubnet(peer_client, &sr->that.client) + && (!sr->this.protocol || transport_proto == sr->this.protocol) + && (!sr->this.port || our_port == sr->this.port) + && (!sr->that.port || peer_port == sr->that.port)) + { + char cib[CONN_INST_BUF]; + char cib2[CONN_INST_BUF]; + + policy_prio_t prio = 8 * (c->prio + (c->kind == CK_INSTANCE)) + + 2 * (sr->this.port == our_port) + + 2 * (sr->that.port == peer_port) + + (sr->this.protocol == transport_proto); + +#ifdef DEBUG + if (DBGP(DBG_CONTROL|DBG_CONTROLMORE)) + { + char c_ocb[SUBNETTOT_BUF], c_pcb[SUBNETTOT_BUF]; + + subnettot(&c->spd.this.client, 0, c_ocb, sizeof(c_ocb)); + subnettot(&c->spd.that.client, 0, c_pcb, sizeof(c_pcb)); + DBG_log("find_connection: conn \"%s\"%s has compatible peers: %s->%s [pri: %ld]" + , c->name + , (fmt_conn_instance(c, cib), cib) + , c_ocb, c_pcb, prio); + } +#endif /* DEBUG */ + + if (best == NULL) + { + best = c; + best_sr = sr; + best_prio = prio; + } + + DBG(DBG_CONTROLMORE, + DBG_log("find_connection: " + "comparing best \"%s\"%s [pri:%ld]{%p} (child %s) to \"%s\"%s [pri:%ld]{%p} (child %s)" + , best->name + , (fmt_conn_instance(best, cib), cib) + , best_prio + , best + , (best->policy_next ? best->policy_next->name : "none") + , c->name + , (fmt_conn_instance(c, cib2), cib2) + , prio + , c + , (c->policy_next ? c->policy_next->name : "none"))); + + if (prio > best_prio) + { + best = c; + best_sr = sr; + best_prio = prio; + } + } + } + } + + if (best!= NULL && NEVER_NEGOTIATE(best->policy)) + best = NULL; + + if (srp != NULL && best != NULL) + *srp = best_sr; + +#ifdef DEBUG + if (DBGP(DBG_CONTROL)) + { + if (best) + { + char cib[CONN_INST_BUF]; + DBG_log("find_connection: concluding with \"%s\"%s [pri:%ld]{%p} kind=%s" + , best->name + , (fmt_conn_instance(best, cib), cib) + , best_prio + , best + , enum_name(&connection_kind_names, best->kind)); + } else { + DBG_log("find_connection: concluding with empty"); + } + } +#endif /* DEBUG */ + + return best; +} + +/* Find and instantiate a connection for an outgoing Opportunistic connection. + * We've already discovered its gateway. + * We look for a the connection such that: + * + this is one of our interfaces + * + this subnet contains our_client (or we are our_client) + * (we will specialize the client). We prefer the smallest such subnet. + * + that subnet contains peer_clent (we will specialize the client). + * We prefer the smallest such subnet. + * + is opportunistic + * + that peer is NO_IP + * + don't care about Phase 1 IDs (probably should be default) + * We could look for a connection that already had the desired peer + * (rather than NO_IP) specified, but it doesn't seem worth the + * bother. + * + * We look for the routed policy applying to the narrowest subnets. + * We only succeed if we find such a policy AND it is satisfactory. + * + * The body of the inner loop is a lot like that in + * find_connection_for_clients. In this case, we know the gateways + * that we need to instantiate an opportunistic connection. + */ +struct connection * +build_outgoing_opportunistic_connection(struct gw_info *gw + ,const ip_address *our_client + ,const ip_address *peer_client) +{ + struct iface *p; + struct connection *best = NULL; + struct spd_route *sr, *bestsr; + char ocb[ADDRTOT_BUF], pcb[ADDRTOT_BUF]; + + addrtot(our_client, 0, ocb, sizeof(ocb)); + addrtot(peer_client, 0, pcb, sizeof(pcb)); + + passert(!isanyaddr(our_client) && !isanyaddr(peer_client)); + + /* We don't know his ID yet, so gw id must be an ipaddr */ + passert(gw->key != NULL); + passert(id_is_ipaddr(&gw->gw_id)); + + /* for each of our addresses... */ + for (p = interfaces; p != NULL; p = p->next) + { + /* go through those connections with our address and NO_IP as hosts + * We cannot know what port the peer would use, so we assume + * that it is pluto_port (makes debugging easier). + */ + struct connection *c = find_host_pair_connections(&p->addr + , pluto_port, (ip_address *)NULL, pluto_port); + + for (; c != NULL; c = c->hp_next) + { + DBG(DBG_OPPO, + DBG_log("checking %s", c->name)); + if (c->kind == CK_GROUP) + { + continue; + } + + for (sr = &c->spd; best!=c && sr; sr = sr->next) + { + if (routed(sr->routing) + && addrinsubnet(our_client, &sr->this.client) + && addrinsubnet(peer_client, &sr->that.client)) + { + if (best == NULL) + { + best = c; + break; + } + + DBG(DBG_OPPO, + DBG_log("comparing best %s to %s" + , best->name, c->name)); + + for (bestsr = &best->spd; best!=c && bestsr; bestsr=bestsr->next) + { + if (!subnetinsubnet(&bestsr->this.client, &sr->this.client) + || (samesubnet(&bestsr->this.client, &sr->this.client) + && !subnetinsubnet(&bestsr->that.client + , &sr->that.client))) + { + best = c; + } + } + } + } + } + } + + if (best == NULL + || NEVER_NEGOTIATE(best->policy) + || (best->policy & POLICY_OPPO) == LEMPTY + || best->kind != CK_TEMPLATE) + return NULL; + else + return oppo_instantiate(best, &gw->gw_id.ip_addr, NULL, gw + , our_client, peer_client); +} + +bool +orient(struct connection *c) +{ + struct spd_route *sr; + + if (!oriented(*c)) + { + struct iface *p; + + for (sr = &c->spd; sr; sr = sr->next) + { + /* Note: this loop does not stop when it finds a match: + * it continues checking to catch any ambiguity. + */ + for (p = interfaces; p != NULL; p = p->next) + { + if (p->ike_float) + continue; + + for (;;) + { + /* check if this interface matches this end */ + if (sameaddr(&sr->this.host_addr, &p->addr) + && (!no_klips || sr->this.host_port == pluto_port)) + { + if (oriented(*c)) + { + if (c->interface == p) + loglog(RC_LOG_SERIOUS + , "both sides of \"%s\" are our interface %s!" + , c->name, p->rname); + else + loglog(RC_LOG_SERIOUS, "two interfaces match \"%s\" (%s, %s)" + , c->name, c->interface->rname, p->rname); + c->interface = NULL; /* withdraw orientation */ + return FALSE; + } + c->interface = p; + } + + /* done with this interface if it doesn't match that end */ + if (!(sameaddr(&sr->that.host_addr, &p->addr) + && (!no_klips || sr->that.host_port == pluto_port))) + break; + + /* swap ends and try again. + * It is a little tricky to see that this loop will stop. + * Only continue if the far side matches. + * If both sides match, there is an error-out. + */ + { + struct end t = sr->this; + + sr->this = sr->that; + sr->that = t; + } + } + } + } + } + return oriented(*c); +} + +void +initiate_connection(const char *name, int whackfd) +{ + struct connection *c = con_by_name(name, TRUE); + + if (c != NULL && c->ikev1) + { + set_cur_connection(c); + if (!oriented(*c)) + { + loglog(RC_ORIENT, "we have no ipsecN interface for either end of this connection"); + } + else if (NEVER_NEGOTIATE(c->policy)) + { + loglog(RC_INITSHUNT + , "cannot initiate an authby=never connection"); + } + else if (c->kind != CK_PERMANENT) + { + if (isanyaddr(&c->spd.that.host_addr)) + loglog(RC_NOPEERIP, "cannot initiate connection without knowing peer IP address"); + else + loglog(RC_WILDCARD, "cannot initiate connection with ID wildcards"); + } + else + { + /* We will only request an IPsec SA if policy isn't empty + * (ignoring Main Mode items). + * This is a fudge, but not yet important. + * If we are to proceed asynchronously, whackfd will be NULL_FD. + */ + c->policy |= POLICY_UP; + /* do we have to prompt for a PIN code? */ + if (c->spd.this.sc != NULL && !c->spd.this.sc->valid && whackfd != NULL_FD) + scx_get_pin(c->spd.this.sc, whackfd); + + if (c->spd.this.sc != NULL && !c->spd.this.sc->valid) + { + loglog(RC_NOVALIDPIN, "cannot initiate connection without valid PIN"); + } + else + { + ipsecdoi_initiate(whackfd, c, c->policy, 1, SOS_NOBODY); + whackfd = NULL_FD; /* protect from close */ + } + } + reset_cur_connection(); + } + close_any(whackfd); +} + +/* (Possibly) Opportunistic Initiation: + * Knowing clients (single IP addresses), try to build an tunnel. + * This may involve discovering a gateway and instantiating an + * Opportunistic connection. Called when a packet is caught by + * a %trap, or when whack --oppohere --oppothere is used. + * It may turn out that an existing or non-opporunistic connnection + * can handle the traffic. + * + * Most of the code will be restarted if an ADNS request is made + * to discover the gateway. The only difference between the first + * and second entry is whether gateways_from_dns is NULL or not. + * initiate_opportunistic: initial entrypoint + * continue_oppo: where we pickup when ADNS result arrives + * initiate_opportunistic_body: main body shared by above routines + * cannot_oppo: a helper function to log a diagnostic + * This structure repeats a lot of code when the ADNS result arrives. + * This seems like a waste, but anything learned the first time through + * may no longer be true! + * + * After the first IKE message is sent, the regular state machinery + * carries negotiation forward. + */ + +enum find_oppo_step { + fos_start, + fos_myid_ip_txt, + fos_myid_hostname_txt, + fos_myid_ip_key, + fos_myid_hostname_key, + fos_our_client, + fos_our_txt, +#ifdef USE_KEYRR + fos_our_key, +#endif /* USE_KEYRR */ + fos_his_client, + fos_done +}; + +#ifdef DEBUG +static const char *const oppo_step_name[] = { + "fos_start", + "fos_myid_ip_txt", + "fos_myid_hostname_txt", + "fos_myid_ip_key", + "fos_myid_hostname_key", + "fos_our_client", + "fos_our_txt", +#ifdef USE_KEYRR + "fos_our_key", +#endif /* USE_KEYRR */ + "fos_his_client", + "fos_done" +}; +#endif /* DEBUG */ + +struct find_oppo_bundle { + enum find_oppo_step step; + err_t want; + bool failure_ok; /* if true, continue_oppo should not die on DNS failure */ + ip_address our_client; /* not pointer! */ + ip_address peer_client; + int transport_proto; + bool held; + policy_prio_t policy_prio; + ipsec_spi_t failure_shunt; /* in host order! 0 for delete. */ + int whackfd; +}; + +struct find_oppo_continuation { + struct adns_continuation ac; /* common prefix */ + struct find_oppo_bundle b; +}; + +static void +cannot_oppo(struct connection *c + , struct find_oppo_bundle *b + , err_t ugh) +{ + char pcb[ADDRTOT_BUF]; + char ocb[ADDRTOT_BUF]; + + addrtot(&b->peer_client, 0, pcb, sizeof(pcb)); + addrtot(&b->our_client, 0, ocb, sizeof(ocb)); + + DBG(DBG_DNS | DBG_OPPO, DBG_log("Can't Opportunistically initiate for %s to %s: %s" + , ocb, pcb, ugh)); + + whack_log(RC_OPPOFAILURE + , "Can't Opportunistically initiate for %s to %s: %s" + , ocb, pcb, ugh); + + if (c != NULL && c->policy_next != NULL) + { + /* there is some policy that comes afterwards */ + struct spd_route *shunt_spd; + struct connection *nc = c->policy_next; + struct state *st; + + passert(c->kind == CK_TEMPLATE); + passert(c->policy_next->kind == CK_PERMANENT); + + DBG(DBG_OPPO, DBG_log("OE failed for %s to %s, but %s overrides shunt" + , ocb, pcb, c->policy_next->name)); + + /* + * okay, here we need add to the "next" policy, which is ought + * to be an instance. + * We will add another entry to the spd_route list for the specific + * situation that we have. + */ + + shunt_spd = clone_thing(nc->spd, "shunt eroute policy"); + + shunt_spd->next = nc->spd.next; + nc->spd.next = shunt_spd; + + happy(addrtosubnet(&b->peer_client, &shunt_spd->that.client)); + + if (sameaddr(&b->peer_client, &shunt_spd->that.host_addr)) + shunt_spd->that.has_client = FALSE; + + /* + * override the tunnel destination with the one from the secondaried + * policy + */ + shunt_spd->that.host_addr = nc->spd.that.host_addr; + + /* now, lookup the state, and poke it up. + */ + + st = state_with_serialno(nc->newest_ipsec_sa); + + /* XXX what to do if the IPSEC SA has died? */ + passert(st != NULL); + + /* link the new connection instance to the state's list of + * connections + */ + + DBG(DBG_OPPO, DBG_log("installing state: %ld for %s to %s" + , nc->newest_ipsec_sa + , ocb, pcb)); + +#ifdef DEBUG + if (DBGP(DBG_OPPO | DBG_CONTROLMORE)) + { + char state_buf[LOG_WIDTH]; + char state_buf2[LOG_WIDTH]; + time_t n = now(); + + fmt_state(FALSE, st, n + , state_buf, sizeof(state_buf) + , state_buf2, sizeof(state_buf2)); + DBG_log("cannot_oppo, failure SA1: %s", state_buf); + DBG_log("cannot_oppo, failure SA2: %s", state_buf2); + } +#endif /* DEBUG */ + + if (!route_and_eroute(c, shunt_spd, st)) + { + whack_log(RC_OPPOFAILURE + , "failed to instantiate shunt policy %s for %s to %s" + , c->name + , ocb, pcb); + } + return; + } + +#ifdef KLIPS + if (b->held) + { + /* Replace HOLD with b->failure_shunt. + * If no b->failure_shunt specified, use SPI_PASS -- THIS MAY CHANGE. + */ + if (b->failure_shunt == 0) + { + DBG(DBG_OPPO, DBG_log("no explicit failure shunt for %s to %s; installing %%pass" + , ocb, pcb)); + } + + (void) replace_bare_shunt(&b->our_client, &b->peer_client + , b->policy_prio + , b->failure_shunt + , b->failure_shunt != 0 + , b->transport_proto + , ugh); + } +#endif +} + +static void initiate_opportunistic_body(struct find_oppo_bundle *b + , struct adns_continuation *ac, err_t ac_ugh); /* forward */ + +void +initiate_opportunistic(const ip_address *our_client +, const ip_address *peer_client +, int transport_proto +, bool held +, int whackfd) +{ + struct find_oppo_bundle b; + + b.want = (whackfd == NULL_FD ? "whack" : "acquire"); + b.failure_ok = FALSE; + b.our_client = *our_client; + b.peer_client = *peer_client; + b.transport_proto = transport_proto; + b.held = held; + b.policy_prio = BOTTOM_PRIO; + b.failure_shunt = 0; + b.whackfd = whackfd; + b.step = fos_start; + initiate_opportunistic_body(&b, NULL, NULL); +} + +static void +continue_oppo(struct adns_continuation *acr, err_t ugh) +{ + struct find_oppo_continuation *cr = (void *)acr; /* inherit, damn you! */ + struct connection *c; + bool was_held = cr->b.held; + int whackfd = cr->b.whackfd; + + /* note: cr->id has no resources; cr->sgw_id is id_none: + * neither need freeing. + */ + whack_log_fd = whackfd; + +#ifdef KLIPS + /* Discover and record whether %hold has gone away. + * This could have happened while we were awaiting DNS. + * We must check BEFORE any call to cannot_oppo. + */ + if (was_held) + cr->b.held = has_bare_hold(&cr->b.our_client, &cr->b.peer_client + , cr->b.transport_proto); +#endif + +#ifdef DEBUG + /* if we're going to ignore the error, at least note it in debugging log */ + if (cr->b.failure_ok && ugh != NULL) + { + DBG(DBG_CONTROL | DBG_DNS, + { + char ocb[ADDRTOT_BUF]; + char pcb[ADDRTOT_BUF]; + + addrtot(&cr->b.our_client, 0, ocb, sizeof(ocb)); + addrtot(&cr->b.peer_client, 0, pcb, sizeof(pcb)); + DBG_log("continuing from failed DNS lookup for %s, %s to %s: %s" + , cr->b.want, ocb, pcb, ugh); + }); + } +#endif + + if (!cr->b.failure_ok && ugh != NULL) + { + c = find_connection_for_clients(NULL, &cr->b.our_client, &cr->b.peer_client + , cr->b.transport_proto); + cannot_oppo(c, &cr->b + , builddiag("%s: %s", cr->b.want, ugh)); + } + else if (was_held && !cr->b.held) + { + /* was_held indicates we were started due to a %trap firing + * (as opposed to a "whack --oppohere --oppothere"). + * Since the %hold has gone, we can assume that somebody else + * has beaten us to the punch. We can go home. But lets log it. + */ + char ocb[ADDRTOT_BUF]; + char pcb[ADDRTOT_BUF]; + + addrtot(&cr->b.our_client, 0, ocb, sizeof(ocb)); + addrtot(&cr->b.peer_client, 0, pcb, sizeof(pcb)); + + loglog(RC_COMMENT + , "%%hold otherwise handled during DNS lookup for Opportunistic Initiation for %s to %s" + , ocb, pcb); + } + else + { + initiate_opportunistic_body(&cr->b, &cr->ac, ugh); + whackfd = NULL_FD; /* was handed off */ + } + + whack_log_fd = NULL_FD; + close_any(whackfd); +} + +#ifdef USE_KEYRR +static err_t +check_key_recs(enum myid_state try_state +, const struct connection *c +, struct adns_continuation *ac) +{ + /* Check if KEY lookup yielded good results. + * Looking up based on our ID. Used if + * client is ourself, or if TXT had no public key. + * Note: if c is different this time, there is + * a chance that we did the wrong query. + * If so, treat as a kind of failure. + */ + enum myid_state old_myid_state = myid_state; + const struct RSA_private_key *our_RSA_pri; + err_t ugh = NULL; + + myid_state = try_state; + + if (old_myid_state != myid_state + && old_myid_state == MYID_SPECIFIED) + { + ugh = "%myid was specified while we were guessing"; + } + else if ((our_RSA_pri = get_RSA_private_key(c)) == NULL) + { + ugh = "we don't know our own RSA key"; + } + else if (!same_id(&ac->id, &c->spd.this.id)) + { + ugh = "our ID changed underfoot"; + } + else + { + /* Similar to code in RSA_check_signature + * for checking the other side. + */ + pubkey_list_t *kr; + + ugh = "no KEY RR found for us"; + for (kr = ac->keys_from_dns; kr != NULL; kr = kr->next) + { + ugh = "all our KEY RRs have the wrong public key"; + if (kr->key->alg == PUBKEY_ALG_RSA + && same_RSA_public_key(&our_RSA_pri->pub, &kr->key->u.rsa)) + { + ugh = NULL; /* good! */ + break; + } + } + } + if (ugh != NULL) + myid_state = old_myid_state; + return ugh; +} +#endif /* USE_KEYRR */ + +static err_t +check_txt_recs(enum myid_state try_state +, const struct connection *c +, struct adns_continuation *ac) +{ + /* Check if TXT lookup yielded good results. + * Looking up based on our ID. Used if + * client is ourself, or if TXT had no public key. + * Note: if c is different this time, there is + * a chance that we did the wrong query. + * If so, treat as a kind of failure. + */ + enum myid_state old_myid_state = myid_state; + const struct RSA_private_key *our_RSA_pri; + err_t ugh = NULL; + + myid_state = try_state; + + if (old_myid_state != myid_state + && old_myid_state == MYID_SPECIFIED) + { + ugh = "%myid was specified while we were guessing"; + } + else if ((our_RSA_pri = get_RSA_private_key(c)) == NULL) + { + ugh = "we don't know our own RSA key"; + } + else if (!same_id(&ac->id, &c->spd.this.id)) + { + ugh = "our ID changed underfoot"; + } + else + { + /* Similar to code in RSA_check_signature + * for checking the other side. + */ + struct gw_info *gwp; + + ugh = "no TXT RR found for us"; + for (gwp = ac->gateways_from_dns; gwp != NULL; gwp = gwp->next) + { + ugh = "all our TXT RRs have the wrong public key"; + if (gwp->key->alg == PUBKEY_ALG_RSA + && same_RSA_public_key(&our_RSA_pri->pub, &gwp->key->u.rsa)) + { + ugh = NULL; /* good! */ + break; + } + } + } + if (ugh != NULL) + myid_state = old_myid_state; + return ugh; +} + + +/* note: gateways_from_dns must be NULL iff this is the first call */ +static void +initiate_opportunistic_body(struct find_oppo_bundle *b +, struct adns_continuation *ac +, err_t ac_ugh) +{ + struct connection *c; + struct spd_route *sr; + + /* What connection shall we use? + * First try for one that explicitly handles the clients. + */ + DBG(DBG_CONTROL, + { + char ours[ADDRTOT_BUF]; + char his[ADDRTOT_BUF]; + int ourport; + int hisport; + + addrtot(&b->our_client, 0, ours, sizeof(ours)); + addrtot(&b->peer_client, 0, his, sizeof(his)); + ourport = ntohs(portof(&b->our_client)); + hisport = ntohs(portof(&b->peer_client)); + DBG_log("initiate on demand from %s:%d to %s:%d proto=%d state: %s because: %s" + , ours, ourport, his, hisport, b->transport_proto + , oppo_step_name[b->step], b->want); + }); + if (isanyaddr(&b->our_client) || isanyaddr(&b->peer_client)) + { + cannot_oppo(NULL, b, "impossible IP address"); + } + else if ((c = find_connection_for_clients(&sr + , &b->our_client + , &b->peer_client + , b->transport_proto)) == NULL) + { + /* No connection explicitly handles the clients and there + * are no Opportunistic connections -- whine and give up. + * The failure policy cannot be gotten from a connection; we pick %pass. + */ + cannot_oppo(NULL, b, "no routed Opportunistic template covers this pair"); + } + else if (c->kind != CK_TEMPLATE) + { + /* We've found a connection that can serve. + * Do we have to initiate it? + * Not if there is currently an IPSEC SA. + * But if there is an IPSEC SA, then KLIPS would not + * have generated the acquire. So we assume that there isn't one. + * This may be redundant if a non-opportunistic + * negotiation is already being attempted. + */ + + /* If we are to proceed asynchronously, b->whackfd will be NULL_FD. */ + + if(c->kind == CK_INSTANCE) + { + char cib[CONN_INST_BUF]; + /* there is already an instance being negotiated, no nothing */ + DBG(DBG_CONTROL, DBG_log("found existing instance \"%s\"%s, rekeying it" + , c->name + , (fmt_conn_instance(c, cib), cib))); + /* XXX-mcr - return; */ + } + + /* otherwise, there is some kind of static conn that can handle + * this connection, so we initiate it */ + +#ifdef KLIPS + if (b->held) + { + /* what should we do on failure? */ + (void) assign_hold(c, sr, b->transport_proto, &b->our_client, &b->peer_client); + } +#endif + ipsecdoi_initiate(b->whackfd, c, c->policy, 1, SOS_NOBODY); + b->whackfd = NULL_FD; /* protect from close */ + } + else + { + /* We are handling an opportunistic situation. + * This involves several DNS lookup steps that require suspension. + * Note: many facts might change while we're suspended. + * Here be dragons. + * + * The first chunk of code handles the result of the previous + * DNS query (if any). It also selects the kind of the next step. + * The second chunk initiates the next DNS query (if any). + */ + enum find_oppo_step next_step; + err_t ugh = ac_ugh; + char mycredentialstr[BUF_LEN]; + char cib[CONN_INST_BUF]; + + DBG(DBG_CONTROL, DBG_log("creating new instance from \"%s\"%s" + , c->name + , (fmt_conn_instance(c, cib), cib))); + + + idtoa(&sr->this.id, mycredentialstr, sizeof(mycredentialstr)); + + passert(c->policy & POLICY_OPPO); /* can't initiate Road Warrior connections */ + + /* handle any DNS answer; select next step */ + + switch (b->step) + { + case fos_start: + /* just starting out: select first query step */ + next_step = fos_myid_ip_txt; + break; + + case fos_myid_ip_txt: /* TXT for our default IP address as %myid */ + ugh = check_txt_recs(MYID_IP, c, ac); + if (ugh != NULL) + { + /* cannot use our IP as OE identitiy for initiation */ + DBG(DBG_OPPO, DBG_log("can not use our IP (%s:TXT) as identity: %s" + , myid_str[MYID_IP] + , ugh)); + if (!logged_myid_ip_txt_warning) + { + loglog(RC_LOG_SERIOUS + , "can not use our IP (%s:TXT) as identity: %s" + , myid_str[MYID_IP] + , ugh); + logged_myid_ip_txt_warning = TRUE; + } + + next_step = fos_myid_hostname_txt; + ugh = NULL; /* failure can be recovered from */ + } + else + { + /* we can use our IP as OE identity for initiation */ + if (!logged_myid_ip_txt_warning) + { + loglog(RC_LOG_SERIOUS + , "using our IP (%s:TXT) as identity!" + , myid_str[MYID_IP]); + logged_myid_ip_txt_warning = TRUE; + } + + next_step = fos_our_client; + } + break; + + case fos_myid_hostname_txt: /* TXT for our hostname as %myid */ + ugh = check_txt_recs(MYID_HOSTNAME, c, ac); + if (ugh != NULL) + { + /* cannot use our hostname as OE identitiy for initiation */ + DBG(DBG_OPPO, DBG_log("can not use our hostname (%s:TXT) as identity: %s" + , myid_str[MYID_HOSTNAME] + , ugh)); + if (!logged_myid_fqdn_txt_warning) + { + loglog(RC_LOG_SERIOUS + , "can not use our hostname (%s:TXT) as identity: %s" + , myid_str[MYID_HOSTNAME] + , ugh); + logged_myid_fqdn_txt_warning = TRUE; + } +#ifdef USE_KEYRR + next_step = fos_myid_ip_key; + ugh = NULL; /* failure can be recovered from */ +#endif + } + else + { + /* we can use our hostname as OE identity for initiation */ + if (!logged_myid_fqdn_txt_warning) + { + loglog(RC_LOG_SERIOUS + , "using our hostname (%s:TXT) as identity!" + , myid_str[MYID_HOSTNAME]); + logged_myid_fqdn_txt_warning = TRUE; + } + next_step = fos_our_client; + } + break; + +#ifdef USE_KEYRR + case fos_myid_ip_key: /* KEY for our default IP address as %myid */ + ugh = check_key_recs(MYID_IP, c, ac); + if (ugh != NULL) + { + /* cannot use our IP as OE identitiy for initiation */ + DBG(DBG_OPPO, DBG_log("can not use our IP (%s:KEY) as identity: %s" + , myid_str[MYID_IP] + , ugh)); + if (!logged_myid_ip_key_warning) + { + loglog(RC_LOG_SERIOUS + , "can not use our IP (%s:KEY) as identity: %s" + , myid_str[MYID_IP] + , ugh); + logged_myid_ip_key_warning = TRUE; + } + + next_step = fos_myid_hostname_key; + ugh = NULL; /* failure can be recovered from */ + } + else + { + /* we can use our IP as OE identity for initiation */ + if (!logged_myid_ip_key_warning) + { + loglog(RC_LOG_SERIOUS + , "using our IP (%s:KEY) as identity!" + , myid_str[MYID_IP]); + logged_myid_ip_key_warning = TRUE; + } + next_step = fos_our_client; + } + break; + + case fos_myid_hostname_key: /* KEY for our hostname as %myid */ + ugh = check_key_recs(MYID_HOSTNAME, c, ac); + if (ugh != NULL) + { + /* cannot use our IP as OE identitiy for initiation */ + DBG(DBG_OPPO, DBG_log("can not use our hostname (%s:KEY) as identity: %s" + , myid_str[MYID_HOSTNAME] + , ugh)); + if (!logged_myid_fqdn_key_warning) + { + loglog(RC_LOG_SERIOUS + , "can not use our hostname (%s:KEY) as identity: %s" + , myid_str[MYID_HOSTNAME] + , ugh); + logged_myid_fqdn_key_warning = TRUE; + } + + next_step = fos_myid_hostname_key; + ugh = NULL; /* failure can be recovered from */ + } + else + { + /* we can use our IP as OE identity for initiation */ + if (!logged_myid_fqdn_key_warning) + { + loglog(RC_LOG_SERIOUS + , "using our hostname (%s:KEY) as identity!" + , myid_str[MYID_HOSTNAME]); + logged_myid_fqdn_key_warning = TRUE; + } + next_step = fos_our_client; + } + break; +#endif + + case fos_our_client: /* TXT for our client */ + { + /* Our client is not us: we must check the TXT records. + * Note: if c is different this time, there is + * a chance that we did the wrong query. + * If so, treat as a kind of failure. + */ + const struct RSA_private_key *our_RSA_pri = get_RSA_private_key(c); + + next_step = fos_his_client; /* normal situation */ + + passert(sr != NULL); + + if (our_RSA_pri == NULL) + { + ugh = "we don't know our own RSA key"; + } + else if (sameaddr(&sr->this.host_addr, &b->our_client)) + { + /* this wasn't true when we started -- bail */ + ugh = "our IP address changed underfoot"; + } + else if (!same_id(&ac->sgw_id, &sr->this.id)) + { + /* this wasn't true when we started -- bail */ + ugh = "our ID changed underfoot"; + } + else + { + /* Similar to code in quick_inI1_outR1_tail + * for checking the other side. + */ + struct gw_info *gwp; + + ugh = "no TXT RR for our client delegates us"; + for (gwp = ac->gateways_from_dns; gwp != NULL; gwp = gwp->next) + { + passert(same_id(&gwp->gw_id, &sr->this.id)); + + ugh = "TXT RR for our client has wrong key"; + /* If there is a key from the TXT record, + * we count it as a win if we match the key. + * If there was no key, we have a tentative win: + * we need to check our KEY record to be sure. + */ + if (!gwp->gw_key_present) + { + /* Success, but the TXT had no key + * so we must check our our own KEY records. + */ + next_step = fos_our_txt; + ugh = NULL; /* good! */ + break; + } + if (same_RSA_public_key(&our_RSA_pri->pub, &gwp->key->u.rsa)) + { + ugh = NULL; /* good! */ + break; + } + } + } + } + break; + + case fos_our_txt: /* TXT for us */ + { + /* Check if TXT lookup yielded good results. + * Looking up based on our ID. Used if + * client is ourself, or if TXT had no public key. + * Note: if c is different this time, there is + * a chance that we did the wrong query. + * If so, treat as a kind of failure. + */ + const struct RSA_private_key *our_RSA_pri = get_RSA_private_key(c); + + next_step = fos_his_client; /* unless we decide to look for KEY RR */ + + if (our_RSA_pri == NULL) + { + ugh = "we don't know our own RSA key"; + } + else if (!same_id(&ac->id, &c->spd.this.id)) + { + ugh = "our ID changed underfoot"; + } + else + { + /* Similar to code in RSA_check_signature + * for checking the other side. + */ + struct gw_info *gwp; + + ugh = "no TXT RR for us"; + for (gwp = ac->gateways_from_dns; gwp != NULL; gwp = gwp->next) + { + passert(same_id(&gwp->gw_id, &sr->this.id)); + + ugh = "TXT RR for us has wrong key"; + if (gwp->gw_key_present + && same_RSA_public_key(&our_RSA_pri->pub, &gwp->key->u.rsa)) + { + DBG(DBG_CONTROL, + DBG_log("initiate on demand found TXT with right public key at: %s" + , mycredentialstr)); + ugh = NULL; + break; + } + } +#ifdef USE_KEYRR + if (ugh != NULL) + { + /* if no TXT with right key, try KEY */ + DBG(DBG_CONTROL, + DBG_log("will try for KEY RR since initiate on demand found %s: %s" + , ugh, mycredentialstr)); + next_step = fos_our_key; + ugh = NULL; + } +#endif + } + } + break; + +#ifdef USE_KEYRR + case fos_our_key: /* KEY for us */ + { + /* Check if KEY lookup yielded good results. + * Looking up based on our ID. Used if + * client is ourself, or if TXT had no public key. + * Note: if c is different this time, there is + * a chance that we did the wrong query. + * If so, treat as a kind of failure. + */ + const struct RSA_private_key *our_RSA_pri = get_RSA_private_key(c); + + next_step = fos_his_client; /* always */ + + if (our_RSA_pri == NULL) + { + ugh = "we don't know our own RSA key"; + } + else if (!same_id(&ac->id, &c->spd.this.id)) + { + ugh = "our ID changed underfoot"; + } + else + { + /* Similar to code in RSA_check_signature + * for checking the other side. + */ + pubkey_list_t *kr; + + ugh = "no KEY RR found for us (and no good TXT RR)"; + for (kr = ac->keys_from_dns; kr != NULL; kr = kr->next) + { + ugh = "all our KEY RRs have the wrong public key (and no good TXT RR)"; + if (kr->key->alg == PUBKEY_ALG_RSA + && same_RSA_public_key(&our_RSA_pri->pub, &kr->key->u.rsa)) + { + /* do this only once a day */ + if (!logged_txt_warning) + { + loglog(RC_LOG_SERIOUS + , "found KEY RR but not TXT RR for %s. See http://www.freeswan.org/err/txt-change.html." + , mycredentialstr); + logged_txt_warning = TRUE; + } + ugh = NULL; /* good! */ + break; + } + } + } + } + break; +#endif /* USE_KEYRR */ + + case fos_his_client: /* TXT for his client */ + { + /* We've finished last DNS queries: TXT for his client. + * Using the information, try to instantiate a connection + * and start negotiating. + * We now know the peer. The chosing of "c" ignored this, + * so we will disregard its current value. + * !!! We need to randomize the entry in gw that we choose. + */ + next_step = fos_done; /* no more queries */ + + c = build_outgoing_opportunistic_connection(ac->gateways_from_dns + , &b->our_client + , &b->peer_client); + + if (c == NULL) + { + /* We cannot seem to instantiate a suitable connection: + * complain clearly. + */ + char ocb[ADDRTOT_BUF] + , pcb[ADDRTOT_BUF] + , pb[ADDRTOT_BUF]; + + addrtot(&b->our_client, 0, ocb, sizeof(ocb)); + addrtot(&b->peer_client, 0, pcb, sizeof(pcb)); + passert(id_is_ipaddr(&ac->gateways_from_dns->gw_id)); + addrtot(&ac->gateways_from_dns->gw_id.ip_addr, 0, pb, sizeof(pb)); + loglog(RC_OPPOFAILURE + , "no suitable connection for opportunism" + " between %s and %s with %s as peer" + , ocb, pcb, pb); + +#ifdef KLIPS + if (b->held) + { + /* Replace HOLD with PASS. + * The type of replacement *ought* to be + * specified by policy. + */ + (void) replace_bare_shunt(&b->our_client, &b->peer_client + , BOTTOM_PRIO + , SPI_PASS /* fail into PASS */ + , TRUE, b->transport_proto + , "no suitable connection"); + } +#endif + } + else + { + /* If we are to proceed asynchronously, b->whackfd will be NULL_FD. */ + passert(c->kind == CK_INSTANCE); + passert(c->gw_info != NULL); + passert(HAS_IPSEC_POLICY(c->policy)); + passert(LHAS(LELEM(RT_UNROUTED) | LELEM(RT_ROUTED_PROSPECTIVE), c->spd.routing)); +#ifdef KLIPS + if (b->held) + { + /* what should we do on failure? */ + (void) assign_hold(c, &c->spd + , b->transport_proto + , &b->our_client, &b->peer_client); + } +#endif + c->gw_info->key->last_tried_time = now(); + ipsecdoi_initiate(b->whackfd, c, c->policy, 1, SOS_NOBODY); + b->whackfd = NULL_FD; /* protect from close */ + } + } + break; + + default: + bad_case(b->step); + } + + /* the second chunk: initiate the next DNS query (if any) */ + DBG(DBG_CONTROL, + { + char ours[ADDRTOT_BUF]; + char his[ADDRTOT_BUF]; + + addrtot(&b->our_client, 0, ours, sizeof(ours)); + addrtot(&b->peer_client, 0, his, sizeof(his)); + DBG_log("initiate on demand from %s to %s new state: %s with ugh: %s" + , ours, his, oppo_step_name[b->step], ugh ? ugh : "ok"); + }); + + if (ugh != NULL) + { + b->policy_prio = c->prio; + b->failure_shunt = shunt_policy_spi(c, FALSE); + cannot_oppo(c, b, ugh); + } + else if (next_step == fos_done) + { + /* nothing to do */ + } + else + { + /* set up the next query */ + struct find_oppo_continuation *cr = alloc_thing(struct find_oppo_continuation + , "opportunistic continuation"); + struct id id; + + b->policy_prio = c->prio; + b->failure_shunt = shunt_policy_spi(c, FALSE); + cr->b = *b; /* copy; start hand off of whackfd */ + cr->b.failure_ok = FALSE; + cr->b.step = next_step; + + for (sr = &c->spd + ; sr!=NULL && !sameaddr(&sr->this.host_addr, &b->our_client) + ; sr = sr->next) + ; + + if (sr == NULL) + sr = &c->spd; + + /* If a %hold shunt has replaced the eroute for this template, + * record this fact. + */ + if (b->held + && sr->routing == RT_ROUTED_PROSPECTIVE && eclipsable(sr)) + { + sr->routing = RT_ROUTED_ECLIPSED; + eclipse_count++; + } + + /* Switch to issue next query. + * A case may turn out to be unnecessary. If so, it falls + * through to the next case. + * Figuring out what %myid can stand for must be done before + * our client credentials are looked up: we must know what + * the client credentials may use to identify us. + * On the other hand, our own credentials should be looked + * up after our clients in case our credentials are not + * needed at all. + * XXX this is a wasted effort if we don't have credentials + * BUT they are not needed. + */ + switch (next_step) + { + case fos_myid_ip_txt: + if (c->spd.this.id.kind == ID_MYID + && myid_state != MYID_SPECIFIED) + { + cr->b.failure_ok = TRUE; + cr->b.want = b->want = "TXT record for IP address as %myid"; + ugh = start_adns_query(&myids[MYID_IP] + , &myids[MYID_IP] + , T_TXT + , continue_oppo + , &cr->ac); + break; + } + cr->b.step = fos_myid_hostname_txt; + /* fall through */ + + case fos_myid_hostname_txt: + if (c->spd.this.id.kind == ID_MYID + && myid_state != MYID_SPECIFIED) + { +#ifdef USE_KEYRR + cr->b.failure_ok = TRUE; +#else + cr->b.failure_ok = FALSE; +#endif + cr->b.want = b->want = "TXT record for hostname as %myid"; + ugh = start_adns_query(&myids[MYID_HOSTNAME] + , &myids[MYID_HOSTNAME] + , T_TXT + , continue_oppo + , &cr->ac); + break; + } + +#ifdef USE_KEYRR + cr->b.step = fos_myid_ip_key; + /* fall through */ + + case fos_myid_ip_key: + if (c->spd.this.id.kind == ID_MYID + && myid_state != MYID_SPECIFIED) + { + cr->b.failure_ok = TRUE; + cr->b.want = b->want = "KEY record for IP address as %myid (no good TXT)"; + ugh = start_adns_query(&myids[MYID_IP] + , (const struct id *) NULL /* security gateway meaningless */ + , T_KEY + , continue_oppo + , &cr->ac); + break; + } + cr->b.step = fos_myid_hostname_key; + /* fall through */ + + case fos_myid_hostname_key: + if (c->spd.this.id.kind == ID_MYID + && myid_state != MYID_SPECIFIED) + { + cr->b.failure_ok = FALSE; /* last attempt! */ + cr->b.want = b->want = "KEY record for hostname as %myid (no good TXT)"; + ugh = start_adns_query(&myids[MYID_HOSTNAME] + , (const struct id *) NULL /* security gateway meaningless */ + , T_KEY + , continue_oppo + , &cr->ac); + break; + } +#endif + cr->b.step = fos_our_client; + /* fall through */ + + case fos_our_client: /* TXT for our client */ + if (!sameaddr(&c->spd.this.host_addr, &b->our_client)) + { + /* Check that at least one TXT(reverse(b->our_client)) is workable. + * Note: {unshare|free}_id_content not needed for id: ephemeral. + */ + cr->b.want = b->want = "our client's TXT record"; + iptoid(&b->our_client, &id); + ugh = start_adns_query(&id + , &c->spd.this.id /* we are the security gateway */ + , T_TXT + , continue_oppo + , &cr->ac); + break; + } + cr->b.step = fos_our_txt; + /* fall through */ + + case fos_our_txt: /* TXT for us */ + cr->b.failure_ok = b->failure_ok = TRUE; + cr->b.want = b->want = "our TXT record"; + ugh = start_adns_query(&sr->this.id + , &sr->this.id /* we are the security gateway XXX - maybe ignore? mcr */ + , T_TXT + , continue_oppo + , &cr->ac); + break; + +#ifdef USE_KEYRR + case fos_our_key: /* KEY for us */ + cr->b.want = b->want = "our KEY record"; + cr->b.failure_ok = b->failure_ok = FALSE; + ugh = start_adns_query(&sr->this.id + , (const struct id *) NULL /* security gateway meaningless */ + , T_KEY + , continue_oppo + , &cr->ac); + break; +#endif /* USE_KEYRR */ + + case fos_his_client: /* TXT for his client */ + /* note: {unshare|free}_id_content not needed for id: ephemeral */ + cr->b.want = b->want = "target's TXT record"; + cr->b.failure_ok = b->failure_ok = FALSE; + iptoid(&b->peer_client, &id); + ugh = start_adns_query(&id + , (const struct id *) NULL /* security gateway unconstrained */ + , T_TXT + , continue_oppo + , &cr->ac); + break; + + default: + bad_case(next_step); + } + + if (ugh == NULL) + b->whackfd = NULL_FD; /* complete hand-off */ + else + cannot_oppo(c, b, ugh); + } + } + close_any(b->whackfd); +} + +void +terminate_connection(const char *nm) +{ + /* Loop because more than one may match (master and instances) + * But at least one is required (enforced by con_by_name). + */ + struct connection *c = con_by_name(nm, TRUE); + + if (c == NULL || !c->ikev1) + return; + + do + { + struct connection *n = c->ac_next; /* grab this before c might disappear */ + + if (streq(c->name, nm) + && c->kind >= CK_PERMANENT + && !NEVER_NEGOTIATE(c->policy)) + { + set_cur_connection(c); + plog("terminating SAs using this connection"); + c->policy &= ~POLICY_UP; + flush_pending_by_connection(c); + delete_states_by_connection(c, FALSE); + reset_cur_connection(); + } + c = n; + } while (c != NULL); +} + +/* check nexthop safety + * Our nexthop must not be within a routed client subnet, and vice versa. + * Note: we don't think this is true. We think that KLIPS will + * not process a packet output by an eroute. + */ +#ifdef NEVER +//bool +//check_nexthop(const struct connection *c) +//{ +// struct connection *d; +// +// if (addrinsubnet(&c->spd.this.host_nexthop, &c->spd.that.client)) +// { +// loglog(RC_LOG_SERIOUS, "cannot perform routing for connection \"%s\"" +// " because nexthop is within peer's client network", +// c->name); +// return FALSE; +// } +// +// for (d = connections; d != NULL; d = d->next) +// { +// if (d->routing != RT_UNROUTED) +// { +// if (addrinsubnet(&c->spd.this.host_nexthop, &d->spd.that.client)) +// { +// loglog(RC_LOG_SERIOUS, "cannot do routing for connection \"%s\" +// " because nexthop is contained in" +// " existing routing for connection \"%s\"", +// c->name, d->name); +// return FALSE; +// } +// if (addrinsubnet(&d->spd.this.host_nexthop, &c->spd.that.client)) +// { +// loglog(RC_LOG_SERIOUS, "cannot do routing for connection \"%s\" +// " because it contains nexthop of" +// " existing routing for connection \"%s\"", +// c->name, d->name); +// return FALSE; +// } +// } +// } +// return TRUE; +//} +#endif /* NEVER */ + +/* an ISAKMP SA has been established. + * Note the serial number, and release any connections with + * the same peer ID but different peer IP address. + */ +bool uniqueIDs = FALSE; /* --uniqueids? */ + +void +ISAKMP_SA_established(struct connection *c, so_serial_t serial) +{ + c->newest_isakmp_sa = serial; + + /* the connection is now oriented so that we are able to determine + * whether we are a mode config server with a virtual IP to send. + */ + if (!isanyaddr(&c->spd.that.host_srcip) && !c->spd.that.has_natip) + c->spd.that.modecfg = TRUE; + + if (uniqueIDs) + { + /* for all connections: if the same Phase 1 IDs are used + * for a different IP address, unorient that connection. + */ + struct connection *d; + + for (d = connections; d != NULL; ) + { + struct connection *next = d->ac_next; /* might move underneath us */ + + if (d->kind >= CK_PERMANENT + && same_id(&c->spd.this.id, &d->spd.this.id) + && same_id(&c->spd.that.id, &d->spd.that.id) + && (!sameaddr(&c->spd.that.host_addr, &d->spd.that.host_addr) || + (c->spd.that.host_port != d->spd.that.host_port))) + { + release_connection(d, FALSE); + } + d = next; + } + } +} + +/* Find the connection to connection c's peer's client with the + * largest value of .routing. All other things being equal, + * preference is given to c. If none is routed, return NULL. + * + * If erop is non-null, set *erop to a connection sharing both + * our client subnet and peer's client subnet with the largest value + * of .routing. If none is erouted, set *erop to NULL. + * + * The return value is used to find other connections sharing a route. + * *erop is used to find other connections sharing an eroute. + */ +struct connection * +route_owner(struct connection *c + , struct spd_route **srp + , struct connection **erop + , struct spd_route **esrp) +{ + struct connection *d + , *best_ro = c + , *best_ero = c; + struct spd_route *srd, *src; + struct spd_route *best_sr, *best_esr; + enum routing_t best_routing, best_erouting; + + passert(oriented(*c)); + best_sr = NULL; + best_esr = NULL; + best_routing = c->spd.routing; + best_erouting = best_routing; + + for (d = connections; d != NULL; d = d->ac_next) + { + for (srd = &d->spd; srd; srd = srd->next) + { + if (srd->routing == RT_UNROUTED) + continue; + + for (src = &c->spd; src; src=src->next) + { + if (!samesubnet(&src->that.client, &srd->that.client)) + continue; + if (src->that.protocol != srd->that.protocol) + continue; + if (src->that.port != srd->that.port) + continue; + passert(oriented(*d)); + if (srd->routing > best_routing) + { + best_ro = d; + best_sr = srd; + best_routing = srd->routing; + } + + if (!samesubnet(&src->this.client, &srd->this.client)) + continue; + if (src->this.protocol != srd->this.protocol) + continue; + if (src->this.port != srd->this.port) + continue; + if (srd->routing > best_erouting) + { + best_ero = d; + best_esr = srd; + best_erouting = srd->routing; + } + } + } + } + + DBG(DBG_CONTROL, + { + char cib[CONN_INST_BUF]; + err_t m = builddiag("route owner of \"%s\"%s %s:" + , c->name + , (fmt_conn_instance(c, cib), cib) + , enum_name(&routing_story, c->spd.routing)); + + if (!routed(best_ro->spd.routing)) + m = builddiag("%s NULL", m); + else if (best_ro == c) + m = builddiag("%s self", m); + else + m = builddiag("%s \"%s\"%s %s", m + , best_ro->name + , (fmt_conn_instance(best_ro, cib), cib) + , enum_name(&routing_story, best_ro->spd.routing)); + + if (erop != NULL) + { + m = builddiag("%s; eroute owner:", m); + if (!erouted(best_ero->spd.routing)) + m = builddiag("%s NULL", m); + else if (best_ero == c) + m = builddiag("%s self", m); + else + m = builddiag("%s \"%s\"%s %s", m + , best_ero->name + , (fmt_conn_instance(best_ero, cib), cib) + , enum_name(&routing_story, best_ero->spd.routing)); + } + + DBG_log("%s", m); + }); + + if (erop != NULL) + *erop = erouted(best_erouting)? best_ero : NULL; + + if (srp != NULL ) + { + *srp = best_sr; + if (esrp != NULL ) + *esrp = best_esr; + } + + return routed(best_routing)? best_ro : NULL; +} + +/* Find a connection that owns the shunt eroute between subnets. + * There ought to be only one. + * This might get to be a bottleneck -- try hashing if it does. + */ +struct connection * +shunt_owner(const ip_subnet *ours, const ip_subnet *his) +{ + struct connection *c; + struct spd_route *sr; + + for (c = connections; c != NULL; c = c->ac_next) + { + for (sr = &c->spd; sr; sr = sr->next) + { + if (shunt_erouted(sr->routing) + && samesubnet(ours, &sr->this.client) + && samesubnet(his, &sr->that.client)) + return c; + } + } + return NULL; +} + +/* Find some connection with this pair of hosts. + * We don't know enough to chose amongst those available. + * ??? no longer usefully different from find_host_pair_connections + */ +struct connection * +find_host_connection(const ip_address *me, u_int16_t my_port +, const ip_address *him, u_int16_t his_port, lset_t policy) +{ + struct connection *c = find_host_pair_connections(me, my_port, him, his_port); + + if (policy != LEMPTY) + { + lset_t auth_requested = policy & POLICY_ID_AUTH_MASK; + + /* if we have requirements for the policy, + * choose the first matching connection. + */ + while (c != NULL) + { + if (c->policy & auth_requested) + { + break; + } + c = c->hp_next; + } + } + return c; +} + +/* given an up-until-now satisfactory connection, find the best connection + * now that we just got the Phase 1 Id Payload from the peer. + * + * Comments in the code describe the (tricky!) matching criteria. + * Although this routine could handle the initiator case, + * it isn't currently called in this case. + * If it were, it could "upgrade" an Opportunistic Connection + * to a Road Warrior Connection if a suitable Peer ID were found. + * + * In RFC 2409 "The Internet Key Exchange (IKE)", + * in 5.1 "IKE Phase 1 Authenticated With Signatures", describing Main + * Mode: + * + * Initiator Responder + * ----------- ----------- + * HDR, SA --> + * <-- HDR, SA + * HDR, KE, Ni --> + * <-- HDR, KE, Nr + * HDR*, IDii, [ CERT, ] SIG_I --> + * <-- HDR*, IDir, [ CERT, ] SIG_R + * + * In 5.4 "Phase 1 Authenticated With a Pre-Shared Key": + * + * HDR, SA --> + * <-- HDR, SA + * HDR, KE, Ni --> + * <-- HDR, KE, Nr + * HDR*, IDii, HASH_I --> + * <-- HDR*, IDir, HASH_R + * + * refine_host_connection could be called in two case: + * + * - the Responder receives the IDii payload: + * + [PSK] after using PSK to decode this message + * + before sending its IDir payload + * + before using its ID in HASH_R computation + * + [DSig] before using its private key to sign SIG_R + * + before using the Initiator's ID in HASH_I calculation + * + [DSig] before using the Initiator's public key to check SIG_I + * + * - the Initiator receives the IDir payload: + * + [PSK] after using PSK to encode previous message and decode this message + * + after sending its IDii payload + * + after using its ID in HASH_I computation + * + [DSig] after using its private key to sign SIG_I + * + before using the Responder's ID to compute HASH_R + * + [DSig] before using Responder's public key to check SIG_R + * + * refine_host_connection can choose a different connection, as long as + * nothing already used is changed. + * + * In the Initiator case, the particular connection might have been + * specified by whatever provoked Pluto to initiate. For example: + * whack --initiate connection-name + * The advantages of switching connections when we're the Initiator seem + * less important than the disadvantages, so after FreeS/WAN 1.9, we + * don't do this. + */ +struct connection * +refine_host_connection(const struct state *st, const struct id *peer_id +, chunk_t peer_ca) +{ + struct connection *c = st->st_connection; + u_int16_t auth = st->st_oakley.auth; + struct connection *d; + struct connection *best_found = NULL; + lset_t auth_policy; + const chunk_t *psk = NULL; + bool wcpip; /* wildcard Peer IP? */ + + int wildcards, our_pathlen, peer_pathlen; + int best_wildcards = MAX_WILDCARDS; + int best_our_pathlen = MAX_CA_PATH_LEN; + int best_peer_pathlen = MAX_CA_PATH_LEN; + + if (same_id(&c->spd.that.id, peer_id) + && trusted_ca(peer_ca, c->spd.that.ca, &peer_pathlen) + && peer_pathlen == 0 + && match_requested_ca(c->requested_ca, c->spd.this.ca, &our_pathlen) + && our_pathlen == 0) + { + DBG(DBG_CONTROL, + DBG_log("current connection is a full match" + " -- no need to look further"); + ) + return c; + } + + switch (auth) + { + case OAKLEY_PRESHARED_KEY: + auth_policy = POLICY_PSK; + psk = get_preshared_secret(c); + /* It should be virtually impossible to fail to find PSK: + * we just used it to decode the current message! + */ + if (psk == NULL) + return NULL; /* cannot determine PSK! */ + break; + case XAUTHInitPreShared: + case XAUTHRespPreShared: + auth_policy = POLICY_XAUTH_PSK; + psk = get_preshared_secret(c); + if (psk == NULL) + return NULL; /* cannot determine PSK! */ + break; + case OAKLEY_RSA_SIG: + auth_policy = POLICY_RSASIG; + break; + case XAUTHInitRSA: + case XAUTHRespRSA: + auth_policy = POLICY_XAUTH_RSASIG; + break; + default: + bad_case(auth); + } + + /* The current connection won't do: search for one that will. + * First search for one with the same pair of hosts. + * If that fails, search for a suitable Road Warrior or Opportunistic + * connection (i.e. wildcard peer IP). + * We need to match: + * - peer_id (slightly complicated by instantiation) + * - if PSK auth, the key must not change (we used it to decode message) + * - policy-as-used must be acceptable to new connection + */ + d = c->host_pair->connections; + for (wcpip = FALSE; ; wcpip = TRUE) + { + for (; d != NULL; d = d->hp_next) + { + const char *match_name[] = {"no", "ok"}; + + bool matching_id = match_id(peer_id + , &d->spd.that.id, &wildcards); + bool matching_auth = (d->policy & auth_policy) != LEMPTY; + + bool matching_trust = trusted_ca(peer_ca + , d->spd.that.ca, &peer_pathlen); + bool matching_request = match_requested_ca(c->requested_ca + , d->spd.this.ca, &our_pathlen); + bool match = matching_id && matching_auth && + matching_trust && matching_request; + + DBG(DBG_CONTROLMORE, + DBG_log("%s: %s match (id: %s, auth: %s, trust: %s, request: %s)" + , d->name + , match ? "full":" no" + , match_name[matching_id] + , match_name[matching_auth] + , match_name[matching_trust] + , match_name[matching_request]) + ) + + /* do we have a match? */ + if (!match) + continue; + + /* ignore group connections */ + if (d->policy & POLICY_GROUP) + continue; + + if (c->spd.that.host_port != d->spd.that.host_port + && d->kind == CK_INSTANCE) + { + continue; + } + + switch (auth) + { + case OAKLEY_PRESHARED_KEY: + case XAUTHInitPreShared: + case XAUTHRespPreShared: + /* secret must match the one we already used */ + { + const chunk_t *dpsk = get_preshared_secret(d); + + if (dpsk == NULL) + continue; /* no secret */ + + if (psk != dpsk) + if (psk->len != dpsk->len + || memcmp(psk->ptr, dpsk->ptr, psk->len) != 0) + continue; /* different secret */ + } + break; + + case OAKLEY_RSA_SIG: + case XAUTHInitRSA: + case XAUTHRespRSA: + /* + * We must at least be able to find our private key + .*/ + if (d->spd.this.sc == NULL /* no smartcard */ + && get_RSA_private_key(d) == NULL) /* no private key */ + continue; + break; + + default: + bad_case(auth); + } + + /* d has passed all the tests. + * We'll go with it if the Peer ID was an exact match. + */ + if (match && wildcards == 0 && peer_pathlen == 0 && our_pathlen == 0) + return d; + + /* We'll remember it as best_found in case an exact + * match doesn't come along. + */ + if (best_found == NULL || wildcards < best_wildcards + || ((wildcards == best_wildcards && peer_pathlen < best_peer_pathlen) + || (peer_pathlen == best_peer_pathlen && our_pathlen < best_our_pathlen))) + { + best_found = d; + best_wildcards = wildcards; + best_peer_pathlen = peer_pathlen; + best_our_pathlen = our_pathlen; + } + } + if (wcpip) + return best_found; /* been around twice already */ + + /* Starting second time around. + * We're willing to settle for a connection that needs Peer IP + * instantiated: Road Warrior or Opportunistic. + * Look on list of connections for host pair with wildcard Peer IP + */ + d = find_host_pair_connections(&c->spd.this.host_addr, c->spd.this.host_port + , (ip_address *)NULL, c->spd.that.host_port); + } +} + +/** + * With virtual addressing, we must not allow someone to use an already + * used (by another id) addr/net. + */ +static bool +is_virtual_net_used(const ip_subnet *peer_net, const struct id *peer_id) +{ + struct connection *d; + + for (d = connections; d != NULL; d = d->ac_next) + { + switch (d->kind) + { + case CK_PERMANENT: + case CK_INSTANCE: + if ((subnetinsubnet(peer_net,&d->spd.that.client) || + subnetinsubnet(&d->spd.that.client,peer_net)) + && !same_id(&d->spd.that.id, peer_id)) + { + char buf[BUF_LEN]; + char client[SUBNETTOT_BUF]; + + subnettot(peer_net, 0, client, sizeof(client)); + idtoa(&d->spd.that.id, buf, sizeof(buf)); + plog("Virtual IP %s is already used by '%s'", client, buf); + idtoa(peer_id, buf, sizeof(buf)); + plog("Your ID is '%s'", buf); + return TRUE; /* already used by another one */ + } + break; + case CK_GOING_AWAY: + default: + break; + } + } + return FALSE; /* you can safely use it */ +} + +/* find_client_connection: given a connection suitable for ISAKMP + * (i.e. the hosts match), find a one suitable for IPSEC + * (i.e. with matching clients). + * + * If we don't find an exact match (not even our current connection), + * we try for one that still needs instantiation. Try Road Warrior + * abstract connections and the Opportunistic abstract connections. + * This requires inverse instantiation: abstraction. + * + * After failing to find an exact match, we abstract the peer + * to be NO_IP (the wildcard value). This enables matches with + * Road Warrior and Opportunistic abstract connections. + * + * After failing that search, we also abstract the Phase 1 peer ID + * if possible. If the peer's ID was the peer's IP address, we make + * it NO_ID; instantiation will make it the peer's IP address again. + * + * If searching for a Road Warrior abstract connection fails, + * and conditions are suitable, we search for the best Opportunistic + * abstract connection. + * + * Note: in the end, both Phase 1 IDs must be preserved, after any + * instantiation. They are the IDs that have been authenticated. + */ + +#define PATH_WEIGHT 1 +#define WILD_WEIGHT (MAX_CA_PATH_LEN+1) +#define PRIO_WEIGHT (MAX_WILDCARDS+1)*WILD_WEIGHT + +/* fc_try: a helper function for find_client_connection */ +static struct connection * +fc_try(const struct connection *c +, struct host_pair *hp +, const struct id *peer_id +, const ip_subnet *our_net +, const ip_subnet *peer_net +, const u_int8_t our_protocol +, const u_int16_t our_port +, const u_int8_t peer_protocol +, const u_int16_t peer_port +, chunk_t peer_ca +, const ietfAttrList_t *peer_list) +{ + struct connection *d; + struct connection *best = NULL; + policy_prio_t best_prio = BOTTOM_PRIO; + int wildcards, pathlen; + + const bool peer_net_is_host = subnetisaddr(peer_net, &c->spd.that.host_addr); + + for (d = hp->connections; d != NULL; d = d->hp_next) + { + struct spd_route *sr; + + if (d->policy & POLICY_GROUP) + continue; + + if (!(same_id(&c->spd.this.id, &d->spd.this.id) + && match_id(&c->spd.that.id, &d->spd.that.id, &wildcards) + && trusted_ca(peer_ca, d->spd.that.ca, &pathlen) + && group_membership(peer_list, d->name, d->spd.that.groups))) + continue; + + /* compare protocol and ports */ + if (d->spd.this.protocol != our_protocol + || d->spd.this.port != our_port + || d->spd.that.protocol != peer_protocol + || (d->spd.that.port != peer_port && !d->spd.that.has_port_wildcard)) + continue; + + /* non-Opportunistic case: + * our_client must match. + * + * So must peer_client, but the testing is complicated + * by the fact that the peer might be a wildcard + * and if so, the default value of that.client + * won't match the default peer_net. The appropriate test: + * + * If d has a peer client, it must match peer_net. + * If d has no peer client, peer_net must just have peer itself. + */ + + for (sr = &d->spd; best != d && sr != NULL; sr = sr->next) + { + policy_prio_t prio; +#ifdef DEBUG + if (DBGP(DBG_CONTROLMORE)) + { + char s1[SUBNETTOT_BUF],d1[SUBNETTOT_BUF]; + char s3[SUBNETTOT_BUF],d3[SUBNETTOT_BUF]; + + subnettot(our_net, 0, s1, sizeof(s1)); + subnettot(peer_net, 0, d1, sizeof(d1)); + subnettot(&sr->this.client, 0, s3, sizeof(s3)); + subnettot(&sr->that.client, 0, d3, sizeof(d3)); + DBG_log(" fc_try trying " + "%s:%s:%d/%d -> %s:%d/%d vs %s:%s:%d/%d -> %s:%d/%d" + , c->name, s1, c->spd.this.protocol, c->spd.this.port + , d1, c->spd.that.protocol, c->spd.that.port + , d->name, s3, sr->this.protocol, sr->this.port + , d3, sr->that.protocol, sr->that.port); + } +#endif /* DEBUG */ + + if (!samesubnet(&sr->this.client, our_net)) + continue; + + if (sr->that.has_client) + { + if (sr->that.has_client_wildcard) + { + if (!subnetinsubnet(peer_net, &sr->that.client)) + continue; + } + else + { + if (!samesubnet(&sr->that.client, peer_net) && !is_virtual_connection(d)) + continue; + if (is_virtual_connection(d) + && (!is_virtual_net_allowed(d, peer_net, &c->spd.that.host_addr) + || is_virtual_net_used(peer_net, peer_id?peer_id:&c->spd.that.id))) + continue; + } + } + else + { + if (!peer_net_is_host) + continue; + } + + /* We've run the gauntlet -- success: + * We've got an exact match of subnets. + * The connection is feasible, but we continue looking for the best. + * The highest priority wins, implementing eroute-like rule. + * - a routed connection is preferrred + * - given that, the smallest number of ID wildcards are preferred + * - given that, the shortest CA pathlength is preferred + */ + prio = PRIO_WEIGHT * routed(sr->routing) + + WILD_WEIGHT * (MAX_WILDCARDS - wildcards) + + PATH_WEIGHT * (MAX_CA_PATH_LEN - pathlen) + + 1; + if (prio > best_prio) + { + best = d; + best_prio = prio; + } + } + } + + if (best != NULL && NEVER_NEGOTIATE(best->policy)) + best = NULL; + + DBG(DBG_CONTROLMORE, + DBG_log(" fc_try concluding with %s [%ld]" + , (best ? best->name : "none"), best_prio) + ) + return best; +} + +static struct connection * +fc_try_oppo(const struct connection *c +, struct host_pair *hp +, const ip_subnet *our_net +, const ip_subnet *peer_net +, const u_int8_t our_protocol +, const u_int16_t our_port +, const u_int8_t peer_protocol +, const u_int16_t peer_port +, chunk_t peer_ca +, const ietfAttrList_t *peer_list) +{ + struct connection *d; + struct connection *best = NULL; + policy_prio_t best_prio = BOTTOM_PRIO; + int wildcards, pathlen; + + for (d = hp->connections; d != NULL; d = d->hp_next) + { + struct spd_route *sr; + policy_prio_t prio; + + if (d->policy & POLICY_GROUP) + continue; + + if (!(same_id(&c->spd.this.id, &d->spd.this.id) + && match_id(&c->spd.that.id, &d->spd.that.id, &wildcards) + && trusted_ca(peer_ca, d->spd.that.ca, &pathlen) + && group_membership(peer_list, d->name, d->spd.that.groups))) + continue; + + /* compare protocol and ports */ + if (d->spd.this.protocol != our_protocol + || d->spd.this.port != our_port + || d->spd.that.protocol != peer_protocol + || (d->spd.that.port != peer_port && !d->spd.that.has_port_wildcard)) + continue; + + /* Opportunistic case: + * our_net must be inside d->spd.this.client + * and peer_net must be inside d->spd.that.client + * Note: this host_pair chain also has shunt + * eroute conns (clear, drop), but they won't + * be marked as opportunistic. + */ + for (sr = &d->spd; sr != NULL; sr = sr->next) + { +#ifdef DEBUG + if (DBGP(DBG_CONTROLMORE)) + { + char s1[SUBNETTOT_BUF],d1[SUBNETTOT_BUF]; + char s3[SUBNETTOT_BUF],d3[SUBNETTOT_BUF]; + + subnettot(our_net, 0, s1, sizeof(s1)); + subnettot(peer_net, 0, d1, sizeof(d1)); + subnettot(&sr->this.client, 0, s3, sizeof(s3)); + subnettot(&sr->that.client, 0, d3, sizeof(d3)); + DBG_log(" fc_try_oppo trying %s:%s -> %s vs %s:%s -> %s" + , c->name, s1, d1, d->name, s3, d3); + } +#endif /* DEBUG */ + + if (!subnetinsubnet(our_net, &sr->this.client) + || !subnetinsubnet(peer_net, &sr->that.client)) + continue; + + /* The connection is feasible, but we continue looking for the best. + * The highest priority wins, implementing eroute-like rule. + * - our smallest client subnet is preferred (longest mask) + * - given that, his smallest client subnet is preferred + * - given that, a routed connection is preferrred + * - given that, the smallest number of ID wildcards are preferred + * - given that, the shortest CA pathlength is preferred + */ + prio = PRIO_WEIGHT * (d->prio + routed(sr->routing)) + + WILD_WEIGHT * (MAX_WILDCARDS - wildcards) + + PATH_WEIGHT * (MAX_CA_PATH_LEN - pathlen); + if (prio > best_prio) + { + best = d; + best_prio = prio; + } + } + } + + /* if the best wasn't opportunistic, we fail: it must be a shunt */ + if (best != NULL + && (NEVER_NEGOTIATE(best->policy) + || (best->policy & POLICY_OPPO) == LEMPTY)) + { + best = NULL; + } + + DBG(DBG_CONTROLMORE, + DBG_log(" fc_try_oppo concluding with %s [%ld]" + , (best ? best->name : "none"), best_prio) + ) + return best; + +} + +/* + * get the peer's CA and group attributes + */ +chunk_t +get_peer_ca_and_groups(struct connection *c, const ietfAttrList_t **peer_list) +{ + struct state *p1st = find_phase1_state(c, ISAKMP_SA_ESTABLISHED_STATES); + + *peer_list = NULL; + + if (p1st != NULL + && p1st->st_peer_pubkey != NULL + && p1st->st_peer_pubkey->issuer.ptr != NULL) + { + x509acert_t *ac = get_x509acert(p1st->st_peer_pubkey->issuer + , p1st->st_peer_pubkey->serial);; + + if (ac != NULL && verify_x509acert(ac, strict_crl_policy)) + *peer_list = ac->groups; + else + { + DBG(DBG_CONTROL, + DBG_log("no valid attribute cert found") + ) + } + return p1st->st_peer_pubkey->issuer; + } + return empty_chunk; +} + +struct connection * +find_client_connection(struct connection *c +, const ip_subnet *our_net, const ip_subnet *peer_net +, const u_int8_t our_protocol, const u_int16_t our_port +, const u_int8_t peer_protocol, const u_int16_t peer_port) +{ + struct connection *d; + struct spd_route *sr; + + const ietfAttrList_t *peer_list = NULL; + chunk_t peer_ca = get_peer_ca_and_groups(c, &peer_list); + +#ifdef DEBUG + if (DBGP(DBG_CONTROLMORE)) + { + char s1[SUBNETTOT_BUF],d1[SUBNETTOT_BUF]; + + subnettot(our_net, 0, s1, sizeof(s1)); + subnettot(peer_net, 0, d1, sizeof(d1)); + + DBG_log("find_client_connection starting with %s" + , (c ? c->name : "(none)")); + DBG_log(" looking for %s:%d/%d -> %s:%d/%d" + , s1, our_protocol, our_port + , d1, peer_protocol, peer_port); + } +#endif /* DEBUG */ + + /* give priority to current connection + * but even greater priority to a routed concrete connection + */ + { + struct connection *unrouted = NULL; + int srnum = -1; + + for (sr = &c->spd; unrouted == NULL && sr != NULL; sr = sr->next) + { + srnum++; + +#ifdef DEBUG + if (DBGP(DBG_CONTROLMORE)) + { + char s2[SUBNETTOT_BUF],d2[SUBNETTOT_BUF]; + + subnettot(&sr->this.client, 0, s2, sizeof(s2)); + subnettot(&sr->that.client, 0, d2, sizeof(d2)); + DBG_log(" concrete checking against sr#%d %s -> %s" + , srnum, s2, d2); + } +#endif /* DEBUG */ + + if (samesubnet(&sr->this.client, our_net) + && samesubnet(&sr->that.client, peer_net) + && sr->this.protocol == our_protocol + && sr->this.port == our_port + && sr->that.protocol == peer_protocol + && sr->that.port == peer_port + && group_membership(peer_list, c->name, sr->that.groups)) + { + passert(oriented(*c)); + if (routed(sr->routing)) + return c; + + unrouted = c; + } + } + + /* exact match? */ + d = fc_try(c, c->host_pair, NULL, our_net, peer_net + , our_protocol, our_port, peer_protocol, peer_port + , peer_ca, peer_list); + + DBG(DBG_CONTROLMORE, + DBG_log(" fc_try %s gives %s" + , c->name + , (d ? d->name : "none")) + ) + + if (d == NULL) + d = unrouted; + } + + if (d == NULL) + { + /* look for an abstract connection to match */ + struct spd_route *sr; + struct host_pair *hp = NULL; + + for (sr = &c->spd; hp==NULL && sr != NULL; sr = sr->next) + { + hp = find_host_pair(&sr->this.host_addr + , sr->this.host_port + , NULL + , sr->that.host_port); +#ifdef DEBUG + if (DBGP(DBG_CONTROLMORE)) + { + char s2[SUBNETTOT_BUF],d2[SUBNETTOT_BUF]; + + subnettot(&sr->this.client, 0, s2, sizeof(s2)); + subnettot(&sr->that.client, 0, d2, sizeof(d2)); + + DBG_log(" checking hostpair %s -> %s is %s" + , s2, d2 + , (hp ? "found" : "not found")); + } +#endif /* DEBUG */ + } + + if (hp != NULL) + { + /* RW match with actual peer_id or abstract peer_id? */ + d = fc_try(c, hp, NULL, our_net, peer_net + , our_protocol, our_port, peer_protocol, peer_port + , peer_ca, peer_list); + + if (d == NULL + && subnetishost(our_net) + && subnetishost(peer_net)) + { + /* Opportunistic match? + * Always use abstract peer_id. + * Note that later instantiation will result in the same peer_id. + */ + d = fc_try_oppo(c, hp, our_net, peer_net + , our_protocol, our_port, peer_protocol, peer_port + , peer_ca, peer_list); + } + } + } + + DBG(DBG_CONTROLMORE, + DBG_log(" concluding with d = %s" + , (d ? d->name : "none")) + ) + return d; +} + +int +connection_compare(const struct connection *ca +, const struct connection *cb) +{ + int ret; + + /* DBG_log("comparing %s to %s", ca->name, cb->name); */ + + ret = strcasecmp(ca->name, cb->name); + if (ret != 0) + return ret; + + ret = ca->kind - cb->kind; /* note: enum connection_kind behaves like int */ + if (ret != 0) + return ret; + + /* same name, and same type */ + switch (ca->kind) + { + case CK_INSTANCE: + return ca->instance_serial < cb->instance_serial ? -1 + : ca->instance_serial > cb->instance_serial ? 1 + : 0; + + default: + return ca->prio < cb->prio ? -1 + : ca->prio > cb->prio ? 1 + : 0; + } +} + +static int +connection_compare_qsort(const void *a, const void *b) +{ + return connection_compare(*(const struct connection *const *)a + , *(const struct connection *const *)b); +} + +void +show_connections_status(bool all, const char *name) +{ + struct connection *c; + int count, i; + struct connection **array; + + /* make an array of connections, and sort it */ + count = 0; + for (c = connections; c != NULL; c = c->ac_next) + { + if (c->ikev1 && (name == NULL || streq(c->name, name))) + count++; + } + array = alloc_bytes(sizeof(struct connection *)*count, "connection array"); + + count=0; + for (c = connections; c != NULL; c = c->ac_next) + { + if (c->ikev1 && (name == NULL || streq(c->name, name))) + array[count++]=c; + } + + /* sort it! */ + qsort(array, count, sizeof(struct connection *), connection_compare_qsort); + + for (i = 0; i < count; i++) + { + const char *ifn; + char instance[1 + 10 + 1]; + char prio[POLICY_PRIO_BUF]; + + c = array[i]; + + ifn = oriented(*c)? c->interface->rname : ""; + + instance[0] = '\0'; + if (c->kind == CK_INSTANCE && c->instance_serial != 0) + snprintf(instance, sizeof(instance), "[%lu]", c->instance_serial); + + /* show topology */ + { + char topo[CONNECTION_BUF]; + struct spd_route *sr = &c->spd; + int num=0; + + while (sr != NULL) + { + (void) format_connection(topo, sizeof(topo), c, sr); + whack_log(RC_COMMENT, "\"%s\"%s: %s; %s; eroute owner: #%lu" + , c->name, instance, topo + , enum_name(&routing_story, sr->routing) + , sr->eroute_owner); + sr = sr->next; + num++; + } + } + + if (all) + { + /* show CAs if defined */ + if (c->spd.this.ca.ptr != NULL || c->spd.that.ca.ptr != NULL) + { + char this_ca[BUF_LEN], that_ca[BUF_LEN]; + + dntoa_or_null(this_ca, BUF_LEN, c->spd.this.ca, "%any"); + dntoa_or_null(that_ca, BUF_LEN, c->spd.that.ca, "%any"); + + whack_log(RC_COMMENT + , "\"%s\"%s: CAs: '%s'...'%s'" + , c->name + , instance + , this_ca + , that_ca); + } + + /* show group attributes if defined */ + if (c->spd.that.groups != NULL) + { + char buf[BUF_LEN]; + + format_groups(c->spd.that.groups, buf, BUF_LEN); + whack_log(RC_COMMENT + , "\"%s\"%s: groups: %s" + , c->name + , instance + , buf); + } + + whack_log(RC_COMMENT + , "\"%s\"%s: ike_life: %lus; ipsec_life: %lus;" + " rekey_margin: %lus; rekey_fuzz: %lu%%; keyingtries: %lu" + , c->name + , instance + , (unsigned long) c->sa_ike_life_seconds + , (unsigned long) c->sa_ipsec_life_seconds + , (unsigned long) c->sa_rekey_margin + , (unsigned long) c->sa_rekey_fuzz + , (unsigned long) c->sa_keying_tries); + + /* show DPD parameters if defined */ + + if (c->dpd_action != DPD_ACTION_NONE) + whack_log(RC_COMMENT + , "\"%s\"%s: dpd_action: %s;" + " dpd_delay: %lus; dpd_timeout: %lus;" + , c->name + , instance + , enum_show(&dpd_action_names, c->dpd_action) + , (unsigned long) c->dpd_delay + , (unsigned long) c->dpd_timeout); + + if (c->policy_next) + { + whack_log(RC_COMMENT + , "\"%s\"%s: policy_next: %s" + , c->name, instance, c->policy_next->name); + } + + /* Note: we display key_from_DNS_on_demand as if policy [lr]KOD */ + fmt_policy_prio(c->prio, prio); + whack_log(RC_COMMENT + , "\"%s\"%s: policy: %s%s%s; prio: %s; interface: %s; " + , c->name + , instance + , prettypolicy(c->policy) + , c->spd.this.key_from_DNS_on_demand? "+lKOD" : "" + , c->spd.that.key_from_DNS_on_demand? "+rKOD" : "" + , prio + , ifn); + } + + whack_log(RC_COMMENT + , "\"%s\"%s: newest ISAKMP SA: #%ld; newest IPsec SA: #%ld; " + , c->name + , instance + , c->newest_isakmp_sa + , c->newest_ipsec_sa); + + if (all) + { + ike_alg_show_connection(c, instance); + kernel_alg_show_connection(c, instance); + } + } + if (count > 0) + whack_log(RC_COMMENT, BLANK_FORMAT); /* spacer */ + + pfree(array); +} + +/* struct pending, the structure representing Quick Mode + * negotiations delayed until a Keying Channel has been negotiated. + * Essentially, a pending call to quick_outI1. + */ + +struct pending { + int whack_sock; + struct state *isakmp_sa; + struct connection *connection; + lset_t policy; + unsigned long try; + so_serial_t replacing; + + struct pending *next; +}; + +/* queue a Quick Mode negotiation pending completion of a suitable Main Mode */ +void +add_pending(int whack_sock +, struct state *isakmp_sa +, struct connection *c +, lset_t policy +, unsigned long try +, so_serial_t replacing) +{ + bool already_queued = FALSE; + struct pending *p = c->host_pair->pending; + + while (p != NULL) + { + if (streq(c->name, p->connection->name)) + { + already_queued = TRUE; + break; + } + p = p->next; + } + DBG(DBG_CONTROL, + DBG_log("Queuing pending Quick Mode with %s \"%s\"%s" + , ip_str(&c->spd.that.host_addr) + , c->name + , already_queued? " already done" : "") + ) + if (already_queued) + return; + + p = alloc_thing(struct pending, "struct pending"); + p->whack_sock = whack_sock; + p->isakmp_sa = isakmp_sa; + p->connection = c; + p->policy = policy; + p->try = try; + p->replacing = replacing; + p->next = c->host_pair->pending; + c->host_pair->pending = p; +} + +/* Release all the whacks awaiting the completion of this state. + * This is accomplished by closing all the whack socket file descriptors. + * We go to a lot of trouble to tell each whack, but to not tell it twice. + */ +void +release_pending_whacks(struct state *st, err_t story) +{ + struct pending *p; + struct stat stst; + + if (st->st_whack_sock == NULL_FD || fstat(st->st_whack_sock, &stst) != 0) + zero(&stst); /* resulting st_dev/st_ino ought to be distinct */ + + release_whack(st); + + for (p = st->st_connection->host_pair->pending; p != NULL; p = p->next) + { + if (p->isakmp_sa == st && p->whack_sock != NULL_FD) + { + struct stat pst; + + if (fstat(p->whack_sock, &pst) == 0 + && (stst.st_dev != pst.st_dev || stst.st_ino != pst.st_ino)) + { + passert(whack_log_fd == NULL_FD); + whack_log_fd = p->whack_sock; + whack_log(RC_COMMENT + , "%s for ISAKMP SA, but releasing whack for pending IPSEC SA" + , story); + whack_log_fd = NULL_FD; + } + close(p->whack_sock); + p->whack_sock = NULL_FD; + } + } +} + +static void +delete_pending(struct pending **pp) +{ + struct pending *p = *pp; + + *pp = p->next; + if (p->connection != NULL) + connection_discard(p->connection); + close_any(p->whack_sock); + pfree(p); +} + +void +unpend(struct state *st) +{ + struct pending **pp + , *p; + + for (pp = &st->st_connection->host_pair->pending; (p = *pp) != NULL; ) + { + if (p->isakmp_sa == st) + { + DBG(DBG_CONTROL, DBG_log("unqueuing pending Quick Mode with %s \"%s\"" + , ip_str(&p->connection->spd.that.host_addr) + , p->connection->name)); + (void) quick_outI1(p->whack_sock, st, p->connection, p->policy + , p->try, p->replacing); + p->whack_sock = NULL_FD; /* ownership transferred */ + p->connection = NULL; /* ownership transferred */ + delete_pending(pp); + } + else + { + pp = &p->next; + } + } +} + +/* a Main Mode negotiation has been replaced; update any pending */ +void +update_pending(struct state *os, struct state *ns) +{ + struct pending *p; + + for (p = os->st_connection->host_pair->pending; p != NULL; p = p->next) + { + if (p->isakmp_sa == os) + p->isakmp_sa = ns; + if (p->connection->spd.this.host_port != ns->st_connection->spd.this.host_port) + { + p->connection->spd.this.host_port = ns->st_connection->spd.this.host_port; + p->connection->spd.that.host_port = ns->st_connection->spd.that.host_port; + } + } +} + +/* a Main Mode negotiation has failed; discard any pending */ +void +flush_pending_by_state(struct state *st) +{ + struct host_pair *hp = st->st_connection->host_pair; + + if (hp != NULL) + { + struct pending **pp + , *p; + + for (pp = &hp->pending; (p = *pp) != NULL; ) + { + if (p->isakmp_sa == st) + delete_pending(pp); + else + pp = &p->next; + } + } +} + +/* a connection has been deleted; discard any related pending */ +static void +flush_pending_by_connection(struct connection *c) +{ + if (c->host_pair != NULL) + { + struct pending **pp + , *p; + + for (pp = &c->host_pair->pending; (p = *pp) != NULL; ) + { + if (p->connection == c) + { + p->connection = NULL; /* prevent delete_pending from releasing */ + delete_pending(pp); + } + else + { + pp = &p->next; + } + } + } +} + +void +show_pending_phase2(const struct host_pair *hp, const struct state *st) +{ + const struct pending *p; + + for (p = hp->pending; p != NULL; p = p->next) + { + if (p->isakmp_sa == st) + { + /* connection-name state-number [replacing state-number] */ + char cip[CONN_INST_BUF]; + + fmt_conn_instance(p->connection, cip); + whack_log(RC_COMMENT, "#%lu: pending Phase 2 for \"%s\"%s replacing #%lu" + , p->isakmp_sa->st_serialno + , p->connection->name + , cip + , p->replacing); + } + } +} + +/* Delete a connection if it is an instance and it is no longer in use. + * We must be careful to avoid circularity: + * we don't touch it if it is CK_GOING_AWAY. + */ +void +connection_discard(struct connection *c) +{ + if (c->kind == CK_INSTANCE) + { + /* see if it is being used by a pending */ + struct pending *p; + + for (p = c->host_pair->pending; p != NULL; p = p->next) + if (p->connection == c) + return; /* in use, so we're done */ + + if (!states_use_connection(c)) + delete_connection(c, FALSE); + } +} + + +/* A template connection's eroute can be eclipsed by + * either a %hold or an eroute for an instance iff + * the template is a /32 -> /32. This requires some special casing. + */ + +long eclipse_count = 0; + +struct connection * +eclipsed(struct connection *c, struct spd_route **esrp) +{ + struct connection *ue; + struct spd_route *sr1 = &c->spd; + + ue = NULL; + + while (sr1 != NULL && ue != NULL) + { + for (ue = connections; ue != NULL; ue = ue->ac_next) + { + struct spd_route *srue = &ue->spd; + + while (srue != NULL + && srue->routing == RT_ROUTED_ECLIPSED + && !(samesubnet(&sr1->this.client, &srue->this.client) + && samesubnet(&sr1->that.client, &srue->that.client))) + { + srue = srue->next; + } + if (srue != NULL && srue->routing==RT_ROUTED_ECLIPSED) + { + *esrp = srue; + break; + } + } + } + return ue; +} + +/* + * Local Variables: + * c-basic-offset:4 + * c-style: pluto + * End: + */ |