diff options
Diffstat (limited to 'src/pluto/connections.c')
-rw-r--r-- | src/pluto/connections.c | 4507 |
1 files changed, 0 insertions, 4507 deletions
diff --git a/src/pluto/connections.c b/src/pluto/connections.c deleted file mode 100644 index 27cec40fc..000000000 --- a/src/pluto/connections.c +++ /dev/null @@ -1,4507 +0,0 @@ -/* information about connections between hosts and clients - * Copyright (C) 1998-2002 D. Hugh Redelmeier. - * Copyright (C) 2009 Andreas Steffen - Hochschule fuer Technik Rapperswil - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#include <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 "kameipsec.h" - -#include <hydra.h> -#include <credentials/certificates/ac.h> -#include <credentials/keys/private_key.h> - -#include "constants.h" -#include "defs.h" -#include "myid.h" -#include "x509.h" -#include "ca.h" -#include "crl.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 "kernel_alg.h" -#include "nat_traversal.h" -#include "virtual.h" -#include "whack_attribute.h" -#include "modecfg.h" - -static void flush_pending_by_connection(connection_t *c); /* forward */ - -static connection_t *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; - connection_t *connections; /* connections with this pair */ - struct pending *pending; /* awaiting Keying Channel */ - struct host_pair *next; -}; - -static struct host_pair *host_pairs = NULL; - -static connection_t *unoriented_connections = NULL; - -/** - * Check if an id was instantiated by assigning to it the current IP address - */ -bool his_id_was_instantiated(const connection_t *c) -{ - if (c->kind != CK_INSTANCE) - { - return FALSE; - } - if (id_is_ipaddr(c->spd.that.id)) - { - identification_t *host; - bool equal; - - host = identification_create_from_sockaddr((sockaddr_t*)&c->spd.that.host_addr); - equal = host->equals(host, c->spd.that.id); - host->destroy(host); - return equal; - } - else - { - return TRUE; - } -} - -/** - * Check to see that IDs of peers match - */ -bool same_peer_ids(const connection_t *c, const connection_t *d, - identification_t *his_id) -{ - return d->spd.this.id->equals(d->spd.this.id, c->spd.this.id) && - d->spd.that.id->equals(d->spd.that.id, - his_id ? his_id : c->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) - { - 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 connection_t *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) - { - connection_t *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(connection_t *c) -{ - if (oriented(*c)) - { - struct host_pair *hp; - - ip_address his_addr = (c->spd.that.allow_any) - ? *aftoinfo(addrtypeof(&c->spd.that.host_addr))->any - : c->spd.that.host_addr; - - hp = find_host_pair(&c->spd.this.host_addr, c->spd.this.host_port - , &his_addr, c->spd.that.host_port); - - if (hp == NULL) - { - /* no suitable host_pair -- build one */ - hp = malloc_thing(struct host_pair); - hp->me.addr = c->spd.this.host_addr; - hp->him.addr = his_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. - */ -connection_t *con_by_name(const char *nm, bool strict) -{ - connection_t *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) - { - 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(connection_t *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(connection_t *c, bool relations) -{ - modecfg_attribute_t *ca; - connection_t *old_cur_connection; - identification_t *client_id; - - 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(connection_t, 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(connection_t, hp_next, c, unoriented_connections); - } - } - else - { - struct host_pair *hp = c->host_pair; - - list_rm(connection_t, 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); - free(hp); - } - } - if (c->kind != CK_GOING_AWAY) - { - free(c->spd.that.virt); - } - - client_id = (c->xauth_identity) ? c->xauth_identity : c->spd.that.id; - - /* release virtual IP address lease if any */ - if (c->spd.that.modecfg && c->spd.that.pool && - !c->spd.that.host_srcip->is_anyaddr(c->spd.that.host_srcip)) - { - hydra->attributes->release_address(hydra->attributes, c->spd.that.pool, - c->spd.that.host_srcip, client_id); - } - - /* release requested attributes if any */ - if (c->requested) - { - c->requested->destroy_function(c->requested, - (void*)modecfg_attribute_destroy); - } - - /* release other attributes if any */ - if (c->attributes) - { - while (c->attributes->remove_last(c->attributes, (void **)&ca) == SUCCESS) - { - hydra->attributes->release(hydra->attributes, ca->handler, - client_id, ca->type, ca->value); - modecfg_attribute_destroy(ca); - } - c->attributes->destroy(c->attributes); - } - - if (c->kind != CK_GOING_AWAY) - { - whack_attr->del_pool(whack_attr, c->name); - } - - /* free internal data */ -#ifdef DEBUG - cur_debugging = old_cur_debugging; -#endif - free(c->name); - DESTROY_IF(c->xauth_identity); - DESTROY_IF(c->spd.this.id); - DESTROY_IF(c->spd.this.ca); - DESTROY_IF(c->spd.this.groups); - DESTROY_IF(c->spd.this.host_srcip); - free(c->spd.this.updown); - free(c->spd.this.pool); - DESTROY_IF(c->spd.that.id); - DESTROY_IF(c->spd.that.ca); - DESTROY_IF(c->spd.that.groups); - DESTROY_IF(c->spd.that.host_srcip); - free(c->spd.that.updown); - free(c->spd.that.pool); - if (c->requested_ca) - { - c->requested_ca->destroy_offset(c->requested_ca, - offsetof(identification_t, destroy)); - } -#ifdef ADNS - gw_delref(&c->gw_info); -#endif - lock_certs_and_keys("delete_connection"); - cert_release(c->spd.this.cert); - scx_release(c->spd.this.sc); - cert_release(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); - - free(c); -} - -/* Delete connections with the specified name */ -void delete_connections_by_name(const char *name, bool strict) -{ - connection_t *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) - { - delete_connection(connections, TRUE); - } -} - -void release_dead_interfaces(void) -{ - struct host_pair *hp; - - for (hp = host_pairs; hp != NULL; hp = hp->next) - { - connection_t **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 */ - { - connection_t *c = unoriented_connections; - - unoriented_connections = NULL; - - while (c) - { - connection_t *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) - && 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). - */ - connection_t *c = hp->connections; - - hp->connections = NULL; - while (c) - { - connection_t *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; - int af = addrtypeof(&e->host_addr); - - if (af != AF_INET && af != AF_INET6) - { - return "unknown address family in default_end"; - } - - /* default ID to IP (but only if not NO_IP -- WildCard) */ - if (e->id->get_type(e->id) == ID_ANY && !isanyaddr(&e->host_addr)) - { - e->id->destroy(e->id); - e->id = identification_create_from_sockaddr((sockaddr_t*)&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[BUF_LEN]; - 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)) - { - strncpy(client, "?", sizeof(client)); - } - else - { - subnettot(&this->client, 0, client, sizeof(client)); - } - } - else if (this->modecfg && this->host_srcip->is_anyaddr(this->host_srcip)) - { - /* we are mode config client, or a server with a pool */ - client_sep = "==="; - client[0] = '%'; - strncpy(client+1, this->pool ?: "modecfg", sizeof(client)-1); - } - - /* 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 */ - snprintf(host_id, sizeof(host_id), "[%Y]", this->id); - - /* [---hop] */ - hop[0] = '\0'; - hop_sep = ""; - if (that && !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%s" - , open_brackets, client, close_brackets, client_sep - , this->allow_any? "%":"" - , host, host_port, host_id, protoport - , hop_sep, hop); - } - else - { - snprintf(buf, buf_len, "%s%s%s%s%s%s%s%s%s%s%s" - , hop, hop_sep - , this->allow_any? "%":"" - , 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 connection_t *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(connection_t *c) -{ - c->name = clone_str(c->name); - if (c->xauth_identity) - { - c->xauth_identity = c->xauth_identity->clone(c->xauth_identity); - } - c->spd.this.id = c->spd.this.id->clone(c->spd.this.id); - c->spd.this.pool = clone_str(c->spd.this.pool); - c->spd.this.updown = clone_str(c->spd.this.updown); - c->spd.this.host_srcip = c->spd.this.host_srcip->clone(c->spd.this.host_srcip); - scx_share(c->spd.this.sc); - cert_share(c->spd.this.cert); - if (c->spd.this.ca) - { - c->spd.this.ca = c->spd.this.ca->clone(c->spd.this.ca); - } - if (c->spd.this.groups) - { - c->spd.this.groups = c->spd.this.groups->get_ref(c->spd.this.groups); - } - c->spd.that.id = c->spd.that.id->clone(c->spd.that.id); - c->spd.that.pool = clone_str(c->spd.that.pool); - c->spd.that.updown = clone_str(c->spd.that.updown); - c->spd.that.host_srcip = c->spd.that.host_srcip->clone(c->spd.that.host_srcip); - scx_share(c->spd.that.sc); - cert_share(c->spd.that.cert); - if (c->spd.that.ca) - { - c->spd.that.ca = c->spd.that.ca->clone(c->spd.that.ca); - } - if (c->spd.that.groups) - { - c->spd.that.groups = c->spd.that.groups->get_ref(c->spd.that.groups); - } - - /* 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(char *filename, struct end *dst) -{ - time_t notBefore, notAfter; - cert_t *cert = NULL; - certificate_t *certificate; - bool cached_cert = FALSE; - - /* initialize end certificate */ - dst->cert = NULL; - - /* initialize smartcard info record */ - dst->sc = NULL; - - if (filename) - { - if (scx_on_smartcard(filename)) - { - /* load cert from smartcard */ - cert = scx_load_cert(filename, &dst->sc, &cached_cert); - } - else - { - /* load cert from file */ - cert = load_host_cert(filename); - } - } - - if (cert) - { - certificate = cert->cert; - - if (dst->id->get_type(dst->id) == ID_ANY || - !certificate->has_subject(certificate, dst->id)) - { - plog( " id '%Y' not confirmed by certificate, defaulting to '%Y'", - dst->id, certificate->get_subject(certificate)); - dst->id->destroy(dst->id); - dst->id = certificate->get_subject(certificate); - dst->id = dst->id->clone(dst->id); - } - - if (cached_cert) - { - dst->cert = cert; - } - else - { - if (!certificate->get_validity(certificate, NULL, ¬Before, ¬After)) - { - plog("certificate is invalid (valid from %T to %T)", - ¬Before, FALSE, ¬After, FALSE); - cert_free(cert); - return; - } - DBG(DBG_CONTROL, - DBG_log("certificate is valid") - ) - add_public_key_from_cert(cert, notAfter, DAL_LOCAL); - dst->cert = cert_add(cert); - } - certificate = dst->cert->cert; - - /* if no CA is defined, use issuer as default */ - if (dst->ca == NULL && certificate->get_type(certificate) == CERT_X509) - { - identification_t *issuer; - - issuer = certificate->get_issuer(certificate); - dst->ca = issuer->clone(issuer); - } - - /* cache the certificate that was last retrieved from the smartcard */ - if (dst->sc) - { - if (!dst->sc->last_cert || - !certificate->equals(certificate, dst->sc->last_cert->cert)) - { - lock_certs_and_keys("load_end_certificates"); - cert_release(dst->sc->last_cert); - dst->sc->last_cert = dst->cert; - cert_share(dst->cert); - unlock_certs_and_keys("load_end_certificates"); - } - time(&dst->sc->last_load); - } - } - scx_share(dst->sc); - cert_share(dst->cert); -} - -static bool extract_end(struct end *dst, const whack_end_t *src, - const char *name, bool is_left) -{ - bool same_ca = FALSE; - - dst->is_left = is_left; - dst->id = identification_create_from_string(src->id); - dst->ca = NULL; - - /* decode CA distinguished name, if any */ - if (src->ca) - { - if streq(src->ca, "%same") - { - same_ca = TRUE; - } - else if (!streq(src->ca, "%any")) - { - dst->ca = identification_create_from_string(src->ca); - if (dst->ca->get_type(dst->ca) != ID_DER_ASN1_DN) - { - plog("bad CA string '%s', ignored", src->ca); - dst->ca->destroy(dst->ca); - dst->ca = NULL; - } - } - } - - /* load local end certificate and extract ID, if any */ - load_end_certificate(src->cert, dst); - - /* does id has wildcards? */ - dst->has_id_wildcards = dst->id->contains_wildcards(dst->id); - - /* decode group attributes, if any */ - if (src->groups) - { - dst->groups = ietf_attributes_create_from_string(src->groups); - } - - /* the rest is simple copying of corresponding fields */ - dst->host_addr = src->host_addr; - dst->host_nexthop = src->host_nexthop; - dst->host_srcip = host_create_from_sockaddr((sockaddr_t*)&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->allow_any = src->allow_any; - dst->sendcert = src->sendcert; - dst->updown = clone_str(src->updown); - dst->host_port = src->host_port; - - /* if the sourceip netmask is zero a named pool exists */ - if (src->sourceip_mask == 0) - { - dst->pool = clone_str(src->sourceip); - } - - /* if host sourceip is defined but no client is present - * behind the host then set client to sourceip/32 - */ - if (!dst->host_srcip->is_anyaddr(dst->host_srcip) && - !dst->has_natip && !dst->has_client) - { - ip_address addr; - err_t ugh; - - addr = *(ip_address*)dst->host_srcip->get_sockaddr(dst->host_srcip); - ugh = addrtosubnet(&addr, &dst->client); - - if (ugh) - { - 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 */ -} - -connection_t *find_connection_by_reqid(uint32_t reqid) -{ - connection_t *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"); - return 0; /* never reached ... */ -} - -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; - connection_t *c = malloc_thing(connection_t); - - zero(c); - c->name = clone_str(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 kernel does not support IPCOMP" - , c->name); - } - - if (wm->esp) - { - 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 : ""); - - DBG(DBG_CRYPT|DBG_CONTROL, - static char buf[BUF_LEN]="<NULL>"; - - if (c->alg_info_esp) - { - alg_info_snprint(buf, sizeof(buf) - ,(struct alg_info *)c->alg_info_esp); - } - DBG_log("esp proposal: %s", buf); - ) - if (c->alg_info_esp) - { - if (c->alg_info_esp->alg_info_cnt == 0) - { - loglog(RC_LOG_SERIOUS, "got 0 esp transforms"); - } - } - else - { - loglog(RC_LOG_SERIOUS, "syntax error in esp string"); - } - } - - if (wm->ike) - { - 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 : ""); - - DBG(DBG_CRYPT|DBG_CONTROL, - static char buf[BUF_LEN]="<NULL>"; - - if (c->alg_info_ike) - { - alg_info_snprint(buf, sizeof(buf) - , (struct alg_info *)c->alg_info_ike); - } - DBG_log("ike proposal: %s", buf); - ) - if (c->alg_info_ike) - { - if (c->alg_info_ike->alg_info_cnt == 0) - { - loglog(RC_LOG_SERIOUS, "got 0 ike transforms"); - } - } - else - { - loglog(RC_LOG_SERIOUS, "syntax error in ike string"); - } - } - - if (wm->xauth_identity) - { - c->xauth_identity - = identification_create_from_string(wm->xauth_identity); - } - - 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, wm->name, TRUE); - same_rightca = extract_end(&c->spd.that, &wm->right, wm->name, FALSE); - - if (same_rightca && c->spd.this.ca) - { - c->spd.that.ca = c->spd.this.ca->clone(c->spd.this.ca); - } - else if (same_leftca && c->spd.that.ca) - { - c->spd.this.ca = c->spd.that.ca->clone(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 - || c->spd.this.allow_any) - { - struct end t = c->spd.this; - - c->spd.this = c->spd.that; - c->spd.that = t; - } - - c->spd.next = NULL; - c->spd.reqid = wm->reqid ?: gen_reqid(); - - c->spd.mark_in.value = wm->mark_in.value; - c->spd.mark_in.mask = wm->mark_in.mask; - c->spd.mark_out.value = wm->mark_out.value; - c->spd.mark_out.mask = wm->mark_out.mask; - - /* 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 || c->spd.that.allow_any) - { - /* 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; - } - - (void)orient(c); - - /* if rightsourceip defines a subnet then create an in-memory pool */ - if (whack_attr->add_pool(whack_attr, c->name, - c->spd.this.is_left ? &wm->right : &wm->left)) - { - c->spd.that.pool = clone_str(c->name); - c->spd.that.modecfg = TRUE; - c->spd.that.has_client = FALSE; - /* reset the host_srcip so that it gets assigned in modecfg */ - DESTROY_IF(c->spd.that.host_srcip); - c->spd.that.host_srcip = host_create_any(AF_INET); - } - - 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[BUF_LEN]; - - (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 freeing. - */ -char *add_group_instance(connection_t *group, const ip_subnet *target) -{ - char namebuf[100], targetbuf[SUBNETTOT_BUF]; - connection_t *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); - t->name = namebuf; - unshare_connection_strings(t); - name = clone_str(t->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 connection_t *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_ANY), 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 connection_t *instantiate(connection_t *c, const ip_address *him, - u_int16_t his_port, identification_t *his_id) -{ - connection_t *d; - - passert(c->kind == CK_TEMPLATE); - passert(c->spd.next == NULL); - - c->instance_serial++; - d = clone_thing(*c); - d->spd.that.allow_any = FALSE; - - if (his_id) - { - d->spd.that.id = his_id; - d->spd.that.has_id_wildcards = FALSE; - } - unshare_connection_strings(d); - if (d->spd.this.groups) - { - d->spd.this.groups = d->spd.this.groups->get_ref(d->spd.this.groups); - } - if (d->spd.that.groups) - { - d->spd.that.groups = d->spd.that.groups->get_ref(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); - - if (sameaddr(&d->spd.that.host_addr, &d->spd.this.host_nexthop)) - { - d->spd.this.host_nexthop = *him; - } - return d; -} - -connection_t *rw_instantiate(connection_t *c, const ip_address *him, - u_int16_t his_port, const ip_subnet *his_net, - identification_t *his_id) -{ - connection_t *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; -} - -#ifdef ADNS - -connection_t *oppo_instantiate(connection_t *c, const ip_address *him, - identification_t *his_id, struct gw_info *gw, - const ip_address *our_client USED_BY_DEBUG, - const ip_address *peer_client) -{ - connection_t *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[BUF_LEN]; - - (void) format_connection(topo, sizeof(topo), d, &d->spd); - DBG_log("instantiated \"%s\": %s", d->name, topo); - ); - return d; -} - -#endif /* ADNS */ - -/* 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 connection_t *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. - */ -connection_t *find_connection_for_clients(struct spd_route **srp, - const ip_address *our_client, - const ip_address *peer_client, - int transport_proto) -{ - connection_t *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 && NEVER_NEGOTIATE(best->policy)) - { - best = NULL; - } - if (srp && best) - { - *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; -} - -#ifdef ADNS - -/* 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. - */ -connection_t *build_outgoing_opportunistic_connection(struct gw_info *gw, - const ip_address *our_client, - const ip_address *peer_client) -{ - struct iface *p; - connection_t *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)); - - /* 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). - */ - connection_t *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 - { - chunk_t encoding = gw->gw_id->get_encoding(gw->gw_id); - id_type_t type = gw->gw_id->get_type(gw->gw_id); - ip_address ip_addr; - - initaddr(encoding.ptr, encoding.len, - (type == ID_IPV4_ADDR) ? AF_INET : AF_INET6, &ip_addr); - - return oppo_instantiate(best, &ip_addr, NULL, gw, our_client, peer_client); - } -} - -#endif /* ADNS */ - -bool orient(connection_t *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) - && 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) - && 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) -{ - connection_t *c = con_by_name(name, TRUE); - - if (c && 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 && !c->spd.that.allow_any) - { - 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 - { - /* do we have to prompt for a PIN code? */ - if (c->spd.this.sc && !c->spd.this.sc->valid && whackfd != NULL_FD) - { - scx_get_pin(c->spd.this.sc, whackfd); - } - if (c->spd.this.sc && !c->spd.this.sc->valid) - { - loglog(RC_NOVALIDPIN, "cannot initiate connection without valid PIN"); - } - else - { - - if (c->spd.that.allow_any) - { - c = instantiate(c, &c->spd.that.host_addr, - c->spd.that.host_port, c->spd.that.id); - } - - /* 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; - 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(connection_t *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 && c->policy_next) - { - /* there is some policy that comes afterwards */ - struct spd_route *shunt_spd; - connection_t *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_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; - } -} - -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); -} - -#ifdef ADNS - -static void continue_oppo(struct adns_continuation *acr, err_t ugh) -{ - struct find_oppo_continuation *cr = (void *)acr; /* inherit, damn you! */ - connection_t *c; - bool was_held = cr->b.held; - int whackfd = cr->b.whackfd; - - /* note: cr->id has no resources; cr->sgw_id is ID_ANY: - * neither need freeing. - */ - whack_log_fd = whackfd; - -#ifdef DEBUG - /* if we're going to ignore the error, at least note it in debugging log */ - if (cr->b.failure_ok && ugh) - { - 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) - { - 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); -} - -#endif /* ADNS */ - -#ifdef USE_KEYRR -static err_t check_key_recs(enum myid_state try_state, const connection_t *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; - private_key_t *private; - 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 ((private = get_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 - && private->belongs_to(private, &kr->key->public_key)) - { - ugh = NULL; /* good! */ - break; - } - } - } - if (ugh) - { - myid_state = old_myid_state; - } - return ugh; -} -#endif /* USE_KEYRR */ - -#ifdef ADNS - -static err_t check_txt_recs(enum myid_state try_state, const connection_t *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; - private_key_t *private; - 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 ((private = get_private_key(c)) == NULL) - { - ugh = "we don't know our own RSA key"; - } - else if (!ac->id->equals(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) - { - public_key_t *pub_key = gwp->key->public_key; - - ugh = "all our TXT RRs have the wrong public key"; - if (pub_key->get_type(pub_key) == KEY_RSA && - private->belongs_to(private, pub_key)) - { - ugh = NULL; /* good! */ - break; - } - } - } - if (ugh) - { - myid_state = old_myid_state; - } - return ugh; -} - -#endif /* ADNS */ - - -/* 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) -{ - connection_t *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 the kernel 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 */ - - if (b->held) - { - /* what should we do on failure? */ - (void) assign_hold(c, sr, b->transport_proto, &b->our_client, &b->peer_client); - } - ipsecdoi_initiate(b->whackfd, c, c->policy, 1, SOS_NOBODY); - b->whackfd = NULL_FD; /* protect from close */ - } -#ifdef ADNS - 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 = fos_myid_ip_txt; - 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))); - snprintf(mycredentialstr, BUF_LEN, "%Y", sr->this.id); - - /* 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) - { - /* cannot use our IP as OE identitiy for initiation */ - DBG(DBG_OPPO, - DBG_log("can not use our IP (%Y:TXT) as identity: %s", - myids[MYID_IP], ugh)); - if (!logged_myid_ip_txt_warning) - { - loglog(RC_LOG_SERIOUS, - "can not use our IP (%Y:TXT) as identity: %s", - myids[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 (%Y:TXT) as identity!", - myids[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) - { - /* cannot use our hostname as OE identitiy for initiation */ - DBG(DBG_OPPO, - DBG_log("can not use our hostname (%Y:TXT) as identity: %s", - myids[MYID_HOSTNAME], ugh)); - if (!logged_myid_fqdn_txt_warning) - { - loglog(RC_LOG_SERIOUS, - "can not use our hostname (%Y:TXT) as identity: %s", - myids[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 (%Y:TXT) as identity!", - myids[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) - { - /* cannot use our IP as OE identitiy for initiation */ - DBG(DBG_OPPO, - DBG_log("can not use our IP (%Y:KEY) as identity: %s", - myids[MYID_IP], ugh)); - if (!logged_myid_ip_key_warning) - { - loglog(RC_LOG_SERIOUS, - "can not use our IP (%Y:KEY) as identity: %s", - myids[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 (%Y:KEY) as identity!", - myids[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) - { - /* cannot use our IP as OE identitiy for initiation */ - DBG(DBG_OPPO, - DBG_log("can not use our hostname (%Y:KEY) as identity: %s", - myids[MYID_HOSTNAME], ugh)); - if (!logged_myid_fqdn_key_warning) - { - loglog(RC_LOG_SERIOUS, - "can not use our hostname (%Y:KEY) as identity: %s", - myids[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 (%Y:KEY) as identity!", - myids[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. - */ - private_key_t *private = get_private_key(c); - - next_step = fos_his_client; /* normal situation */ - - if (private == 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 (!ac->sgw_id->equals(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) - { - 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 (private->belongs_to(private, gwp->key->public_key)) - { - 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. - */ - private_key_t *private = get_private_key(c); - - next_step = fos_his_client; /* unless we decide to look for KEY RR */ - - if (private == NULL) - { - ugh = "we don't know our own RSA key"; - } - else if (!ac->id->equals(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) - { - ugh = "TXT RR for us has wrong key"; - if (gwp->gw_key_present && - private->belongs_to(private, gwp->key->public_key)) - { - 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) - { - /* 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. - */ - private_key_t *private = get_private_key(c); - - next_step = fos_his_client; /* always */ - - if (private == 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 - && private->belongs_to(private, kr->key->public_key)) - { - /* 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]; - - addrtot(&b->our_client, 0, ocb, sizeof(ocb)); - addrtot(&b->peer_client, 0, pcb, sizeof(pcb)); - loglog(RC_OPPOFAILURE, - "no suitable connection for opportunism " - "between %s and %s with %Y as peer", - ocb, pcb, ac->gateways_from_dns->gw_id); - } - 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)); - if (b->held) - { - /* what should we do on failure? */ - (void) assign_hold(c, &c->spd - , b->transport_proto - , &b->our_client, &b->peer_client); - } - 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) - { - 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 = malloc_thing(struct find_oppo_continuation); - identification_t *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->get_type(c->spd.this.id) == 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->get_type(c->spd.this.id) == 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], 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], 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"; - id = identification_create_from_sockaddr((sockaddr_t*)&b->our_client); - ugh = start_adns_query(id, c->spd.this.id, /* we are the security gateway */ - T_TXT, continue_oppo, &cr->ac); - id->destroy(id); - 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 */ - 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, 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; - id = identification_create_from_sockaddr((sockaddr_t*)&b->peer_client); - ugh = start_adns_query(id, NULL, /* security gateway unconstrained */ - T_TXT, continue_oppo, &cr->ac); - id->destroy(id); - break; - - default: - bad_case(next_step); - } - - if (ugh == NULL) - b->whackfd = NULL_FD; /* complete hand-off */ - else - cannot_oppo(c, b, ugh); - } - } -#endif /* ADNS */ - 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). - */ - connection_t *c = con_by_name(nm, TRUE); - - if (c == NULL || !c->ikev1) - return; - - do - { - connection_t *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); - if (c->kind == CK_INSTANCE) - delete_connection(c, FALSE); - reset_cur_connection(); - } - c = n; - } while (c); -} - -/* 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(connection_t *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 (!c->spd.that.host_srcip->is_anyaddr(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. - */ - connection_t *d; - - for (d = connections; d != NULL; ) - { - connection_t *next = d->ac_next; /* might move underneath us */ - - if (d->kind >= CK_PERMANENT && - c->spd.this.id->equals(c->spd.this.id, d->spd.this.id) && - c->spd.that.id->equals(c->spd.that.id, d->spd.that.id) && - !sameaddr(&c->spd.that.host_addr, &d->spd.that.host_addr)) - { - 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. - */ -connection_t *route_owner(connection_t *c, struct spd_route **srp, - connection_t **erop, struct spd_route **esrp) -{ - connection_t *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; - } - if (src->mark_out.value != srd->mark_out.value) - { - 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 (src->mark_in.value != srd->mark_in.value) - { - 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) - { - 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) - { - *erop = erouted(best_erouting)? best_ero : NULL; - } - if (srp) - { - *srp = best_sr; - if (esrp) - { - *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. - */ -connection_t *shunt_owner(const ip_subnet *ours, const ip_subnet *his) -{ - connection_t *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 - */ -connection_t *find_host_connection(const ip_address *me, u_int16_t my_port, - const ip_address *him, u_int16_t his_port, - lset_t policy) -{ - connection_t *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) - { - 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. - */ -#define PRIO_NO_MATCH_FOUND 2048 - -connection_t *refine_host_connection(const struct state *st, - identification_t *peer_id, - identification_t *peer_ca) -{ - connection_t *c = st->st_connection; - connection_t *d; - connection_t *best_found = NULL; - u_int16_t auth = st->st_oakley.auth; - lset_t auth_policy = POLICY_PSK; - const chunk_t *psk = NULL; - bool wcpip; /* wildcard Peer IP? */ - int best_prio = PRIO_NO_MATCH_FOUND; - int our_pathlen, peer_pathlen; - - if (c->spd.that.id->equals(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: - case OAKLEY_ECDSA_256: - case OAKLEY_ECDSA_384: - case OAKLEY_ECDSA_521: - auth_policy = POLICY_PUBKEY; - 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"}; - - id_match_t match_level = peer_id->matches(peer_id, d->spd.that.id); - - bool matching_id = match_level > ID_MATCH_NONE; - - 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; - - int prio = (ID_MATCH_PERFECT) * !matching_request + - ID_MATCH_PERFECT - match_level; - - prio = (X509_MAX_PATH_LEN + 1) * prio + peer_pathlen; - prio = (X509_MAX_PATH_LEN + 1) * prio + our_pathlen; - - DBG(DBG_CONTROLMORE, - DBG_log("%s: %s match (id: %s, auth: %s, trust: %s, request: %s, prio: %4d)" - , d->name - , match ? "full":" no" - , match_name[matching_id] - , match_name[matching_auth] - , match_name[matching_trust] - , match_name[matching_request] - , match ? prio:PRIO_NO_MATCH_FOUND) - ) - - /* 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 OAKLEY_ECDSA_256: - case OAKLEY_ECDSA_384: - case OAKLEY_ECDSA_521: - case XAUTHInitRSA: - case XAUTHRespRSA: - /* - * We must at least be able to find our private key - .*/ - if (d->spd.this.sc == NULL /* no smartcard */ - && get_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 (prio == 0) - { - return d; - } - - /* We'll remember it as best_found in case an exact - * match doesn't come along. - */ - if (prio < best_prio) - { - best_found = d; - best_prio = prio; - } - } - 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, - identification_t *peer_id) -{ - connection_t *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)) - && !d->spd.that.id->equals(d->spd.that.id, peer_id)) - { - char client[SUBNETTOT_BUF]; - - subnettot(peer_net, 0, client, sizeof(client)); - plog("Virtual IP %s is already used by '%Y'", - client, d->spd.that.id); - plog("Your ID is '%Y'", peer_id); - - 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 (X509_MAX_PATH_LEN+1) -#define PRIO_WEIGHT (ID_MATCH_PERFECT+1) * WILD_WEIGHT - -/* fc_try: a helper function for find_client_connection */ -static connection_t *fc_try(const connection_t *c, struct host_pair *hp, - identification_t *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, - identification_t *peer_ca, - ietf_attributes_t *peer_attributes) -{ - connection_t *d; - connection_t *best = NULL; - policy_prio_t best_prio = BOTTOM_PRIO; - id_match_t match_level; - int 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; - } - - match_level = c->spd.that.id->matches(c->spd.that.id, d->spd.that.id); - - if (!(c->spd.this.id->equals(c->spd.this.id, d->spd.this.id) && - (match_level > ID_MATCH_NONE) && - trusted_ca(peer_ca, d->spd.that.ca, &pathlen) && - match_group_membership(peer_attributes, 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 - { - host_t *vip = c->spd.that.host_srcip; - - if (!peer_net_is_host && !(sr->that.modecfg && c->spd.that.modecfg && - subnetisaddr(peer_net, (ip_address*)vip->get_sockaddr(vip)))) - { - 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 * match_level - + PATH_WEIGHT * (X509_MAX_PATH_LEN - pathlen) - + 1; - if (prio > best_prio) - { - best = d; - best_prio = prio; - } - } - } - - if (best && 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 connection_t *fc_try_oppo(const connection_t *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, - identification_t *peer_ca, - ietf_attributes_t *peer_attributes) -{ - connection_t *d; - connection_t *best = NULL; - policy_prio_t best_prio = BOTTOM_PRIO; - id_match_t match_level; - int 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; - } - match_level = c->spd.that.id->matches(c->spd.that.id, c->spd.that.id); - - if (!(c->spd.this.id->equals(c->spd.this.id, d->spd.this.id) && - (match_level > ID_MATCH_NONE) && - trusted_ca(peer_ca, d->spd.that.ca, &pathlen) && - match_group_membership(peer_attributes, 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 * match_level - + PATH_WEIGHT * (X509_MAX_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 && (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 - */ -void get_peer_ca_and_groups(connection_t *c, - identification_t **peer_ca, - ietf_attributes_t **peer_attributes) -{ - struct state *p1st; - - *peer_ca = NULL; - *peer_attributes = NULL; - - p1st = find_phase1_state(c, ISAKMP_SA_ESTABLISHED_STATES); - if (p1st && p1st->st_peer_pubkey && p1st->st_peer_pubkey->issuer) - { - certificate_t *cert; - - cert = ac_get_cert(p1st->st_peer_pubkey->issuer, - p1st->st_peer_pubkey->serial); - if (cert && ac_verify_cert(cert, strict_crl_policy)) - { - ac_t *ac = (ac_t*)cert; - - *peer_attributes = ac->get_groups(ac); - } - else - { - DBG(DBG_CONTROL, - DBG_log("no valid attribute cert found") - ) - } - *peer_ca = p1st->st_peer_pubkey->issuer; - } -} - -connection_t *find_client_connection(connection_t *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) -{ - connection_t *d; - struct spd_route *sr; - ietf_attributes_t *peer_attributes = NULL; - identification_t *peer_ca; - - get_peer_ca_and_groups(c, &peer_ca, &peer_attributes); - -#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 - */ - { - connection_t *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 - && match_group_membership(peer_attributes, c->name, sr->that.groups)) - { - passert(oriented(*c)); - if (routed(sr->routing)) - { - DESTROY_IF(peer_attributes); - 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_attributes); - - 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) - { - /* 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_attributes); - - 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_attributes); - } - } - } - - DBG(DBG_CONTROLMORE, - DBG_log(" concluding with d = %s" - , (d ? d->name : "none")) - ) - DESTROY_IF(peer_attributes); - return d; -} - -int connection_compare(const connection_t *ca, const connection_t *cb) -{ - int ret; - - /* DBG_log("comparing %s to %s", ca->name, cb->name); */ - - ret = strcasecmp(ca->name, cb->name); - if (ret) - { - return ret; - } - - ret = ca->kind - cb->kind; /* note: enum connection_kind behaves like int */ - if (ret) - { - 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 connection_t *const *)a - , *(const connection_t *const *)b); -} - -void show_connections_status(bool all, const char *name) -{ - connection_t *c; - int count, i; - connection_t **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 = malloc(sizeof(connection_t *)*count); - - 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(connection_t *), 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[BUF_LEN]; - struct spd_route *sr = &c->spd; - int num=0; - - while (sr) - { - (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 && c->spd.that.ca) - { - whack_log(RC_COMMENT, "\"%s\"%s: CAs: \"%Y\"...\"%Y\"", - c->name, instance, c->spd.this.ca, c->spd.that.ca); - } - else if (c->spd.this.ca) - { - whack_log(RC_COMMENT, "\"%s\"%s: CAs: \"%Y\"...%%any", - c->name, instance, c->spd.this.ca); - - } - else if (c->spd.that.ca) - { - whack_log(RC_COMMENT, "\"%s\"%s: CAs: %%any...\"%Y\"", - c->name, instance, c->spd.that.ca); - } - - /* show group attributes if defined */ - if (c->spd.that.groups) - { - whack_log(RC_COMMENT, "\"%s\"%s: groups: %s" - , c->name - , instance - , c->spd.that.groups->get_string(c->spd.that.groups)); - } - - 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: %N;" - " dpd_delay: %lus; dpd_timeout: %lus;" - , c->name - , instance - , 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 */ - - free(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; - connection_t *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, connection_t *c, - lset_t policy, unsigned long try, so_serial_t replacing) -{ - bool already_queued = FALSE; - struct pending *p = c->host_pair->pending; - - while (p) - { - 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 = malloc_thing(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) - { - connection_discard(p->connection); - } - close_any(p->whack_sock); - free(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) - { - 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(connection_t *c) -{ - if (c->host_pair) - { - 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(connection_t *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; - -connection_t *eclipsed(connection_t *c, struct spd_route **esrp) -{ - connection_t *ue; - struct spd_route *sr1 = &c->spd; - - ue = NULL; - - while (sr1 && ue) - { - for (ue = connections; ue != NULL; ue = ue->ac_next) - { - struct spd_route *srue = &ue->spd; - - while (srue && srue->routing == RT_ROUTED_ECLIPSED - && !(samesubnet(&sr1->this.client, &srue->this.client) - && samesubnet(&sr1->that.client, &srue->that.client))) - { - srue = srue->next; - } - if (srue && srue->routing == RT_ROUTED_ECLIPSED) - { - *esrp = srue; - break; - } - } - } - return ue; -} - -/* - * Local Variables: - * c-basic-offset:4 - * c-style: pluto - * End: - */ |