📄 connections.c
字号:
/* information about connections between hosts and clients * Copyright (C) 1998-2002 D. Hugh Redelmeier. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * RCSID $Id: connections.c,v 1.234 2004/11/30 14:02:52 mcr Exp $ */#include <string.h>#include <stdio.h>#include <stddef.h>#include <stdlib.h>#include <unistd.h>#include <netinet/in.h>#include <sys/socket.h>#include <sys/stat.h>#include <netinet/in.h>#include <arpa/inet.h>#include <resolv.h>#include <arpa/nameser.h> /* missing from <resolv.h> on old systems */#include <sys/queue.h>#include <openswan.h>#include <openswan/ipsec_policy.h>#include "kameipsec.h"#include "constants.h"#include "defs.h"#include "id.h"#include "x509.h"#include "pgp.h"#include "certs.h"#include "ac.h"#include "smartcard.h"#ifdef XAUTH_USEPAM#include <security/pam_appl.h>#endif#include "connections.h" /* needs id.h */#include "foodgroups.h"#include "packet.h"#include "demux.h" /* needs packet.h */#include "state.h"#include "timer.h"#include "ipsec_doi.h" /* needs demux.h and state.h */#include "server.h"#include "kernel.h" /* needs connections.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 "plutoalg.h"#include "xauth.h"#ifdef NAT_TRAVERSAL#include "nat_traversal.h"#endif#ifdef VIRTUAL_IP#include "virtual.h"#endifstatic void flush_pending_by_connection(struct connection *c); /* forward */static struct connection *connections = NULL;/* struct host_pair: a nexus of information about a pair of hosts. * A host is an IP address, UDP port pair. This is a debatable choice: * - should port be considered (no choice of port in standard)? * - should ID be considered (hard because not always known)? * - should IP address matter on our end (we don't know our end)? * Only oriented connections are registered. * Unoriented connections are kept on the unoriented_connections * linked list (using hp_next). For them, host_pair is NULL. */struct host_pair { struct { ip_address addr; u_int16_t port; /* host order */ } me, him; bool initial_connection_sent; struct connection *connections; /* connections with this pair */ struct pending *pending; /* awaiting Keying Channel */ struct host_pair *next;};static struct host_pair *host_pairs = NULL;static struct connection *unoriented_connections = NULL;/* check to see that Ids of peers match */boolsame_peer_ids(const struct connection *c, const struct connection *d, const struct id *his_id){ return same_id(&c->spd.this.id, &d->spd.this.id) && same_id(his_id == NULL? &c->spd.that.id : his_id, &d->spd.that.id);}static struct host_pair *find_host_pair(const ip_address *myaddr, u_int16_t myport, const ip_address *hisaddr, u_int16_t hisport){ struct host_pair *p, *prev; /* default hisaddr to an appropriate any */ if (hisaddr == NULL) hisaddr = aftoinfo(addrtypeof(myaddr))->any;#ifdef NAT_TRAVERSAL 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; }#endif for (prev = NULL, p = host_pairs; p != NULL; prev = p, p = p->next) { if (sameaddr(&p->me.addr, myaddr) && p->me.port == myport && sameaddr(&p->him.addr, hisaddr) && p->him.port == hisport) { if (prev != NULL) { prev->next = p->next; /* remove p from list */ p->next = host_pairs; /* and stick it on front */ host_pairs = p; } break; } } return p;}/* find head of list of connections with this pair of hosts */static struct connection *find_host_pair_connections(const ip_address *myaddr, u_int16_t myport, const ip_address *hisaddr, u_int16_t hisport){ struct host_pair *hp = find_host_pair(myaddr, myport, hisaddr, hisport);#ifdef NAT_TRAVERSAL if (nat_traversal_enabled && hp && hisaddr) { struct connection *c; for (c = hp->connections; c != NULL; c = c->hp_next) { if ((c->spd.this.host_port==myport) && (c->spd.that.host_port==hisport)) return c; } return NULL; }#endif return hp == NULL? NULL : hp->connections;}static voidconnect_to_host_pair(struct connection *c){ if (oriented(*c)) { struct host_pair *hp = find_host_pair(&c->spd.this.host_addr, c->spd.this.host_port , &c->spd.that.host_addr, c->spd.that.host_port); if (hp == NULL) { /* no suitable host_pair -- build one */ hp = alloc_thing(struct host_pair, "host_pair"); hp->me.addr = c->spd.this.host_addr; hp->him.addr = c->spd.that.host_addr;#ifdef NAT_TRAVERSAL 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;#else hp->me.port = c->spd.this.host_port; hp->him.port = c->spd.that.host_port;#endif hp->initial_connection_sent = FALSE; hp->connections = NULL; hp->pending = NULL; hp->next = host_pairs; host_pairs = hp; } c->host_pair = hp; c->hp_next = hp->connections; hp->connections = c; } else { /* since this connection isn't oriented, we place it * in the unoriented_connections list instead. */ c->host_pair = NULL; c->hp_next = unoriented_connections; unoriented_connections = c; }}/* find a connection by name. * If strict, don't accept a CK_INSTANCE. * Move the winner (if any) to the front. * If none is found, and strict, a diagnostic is logged to whack. */struct connection *con_by_name(const char *nm, bool strict){ struct connection *p, *prev; for (prev = NULL, p = connections; ; prev = p, p = p->ac_next) { if (p == NULL) { if (strict) whack_log(RC_UNKNOWN_NAME , "no connection named \"%s\"", nm); break; } if (streq(p->name, nm) && (!strict || p->kind != CK_INSTANCE)) { if (prev != NULL) { prev->ac_next = p->ac_next; /* remove p from list */ p->ac_next = connections; /* and stick it on front */ connections = p; } break; } } return p;}voidrelease_connection(struct connection *c, bool relations){ if (c->kind == CK_INSTANCE) { /* This does everything we need. * Note that we will be called recursively by delete_connection, * but kind will be CK_GOING_AWAY. */ delete_connection(c, relations); } else { flush_pending_by_connection(c); delete_states_by_connection(c, relations); unroute_connection(c); }}/* Delete a connection */#define list_rm(etype, enext, e, ehead) { \ etype **ep; \ for (ep = &(ehead); *ep != (e); ep = &(*ep)->enext) \ passert(*ep != NULL); /* we must not come up empty-handed */ \ *ep = (e)->enext; \ }static voiddelete_end(struct connection *c UNUSED, struct spd_route *sr UNUSED, struct end *e){ free_id_content(&e->id); pfreeany(e->updown); freeanychunk(e->ca);#ifdef SMARTCARD scx_release(e->sc);#endif release_cert(e->cert); free_ietfAttrList(e->groups);}static voiddelete_sr(struct connection *c, struct spd_route *sr){ delete_end(c, sr, &sr->this); delete_end(c, sr, &sr->that);}voiddelete_connection(struct connection *c, bool relations){ struct spd_route *sr; struct connection *old_cur_connection = cur_connection == c? NULL : cur_connection;#ifdef DEBUG lset_t old_cur_debugging = cur_debugging;#endif set_cur_connection(c); /* Must be careful to avoid circularity: * we mark c as going away so it won't get deleted recursively. */ passert(c->kind != CK_GOING_AWAY); if (c->kind == CK_INSTANCE) { openswan_log("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 { openswan_log("deleting connection"); } release_connection(c, relations); /* won't delete c */ if (c->kind == CK_GROUP) delete_group(c); /* free up any logging resources */ perpeer_logfree(c); /* find and delete c from connections list */ list_rm(struct connection, ac_next, c, connections); cur_connection = old_cur_connection; /* find and delete c from the host pair list */ if (c->host_pair == NULL) { list_rm(struct connection, hp_next, c, unoriented_connections); } else { struct host_pair *hp = c->host_pair; list_rm(struct connection, hp_next, c, hp->connections); c->host_pair = NULL; /* redundant, but safe */ /* if there are no more connections with this host_pair * and we haven't even made an initial contact, let's delete * this guy in case we were created by an attempted DOS attack. */ if (hp->connections == NULL && !hp->initial_connection_sent) { passert(hp->pending == NULL); /* ??? must deal with this! */ list_rm(struct host_pair, next, hp, host_pairs); pfree(hp); } }#ifdef VIRTUAL_IP if (c->kind != CK_GOING_AWAY) pfreeany(c->spd.that.virt);#endif#ifdef DEBUG set_debugging(old_cur_debugging);#endif pfreeany(c->name); sr = &c->spd; while(sr) { delete_sr(c, sr); sr = sr->next; } free_generalNames(c->requested_ca, TRUE); gw_delref(&c->gw_info);#ifdef KERNEL_ALG alg_info_delref((struct alg_info **)&c->alg_info_esp);#endif#ifdef IKE_ALG alg_info_delref((struct alg_info **)&c->alg_info_ike);#endif pfree(c);}/* Delete connections with the specified name */voiddelete_connections_by_name(const char *name, bool strict){ struct connection *c = con_by_name(name, strict); for (; c != NULL; c = con_by_name(name, FALSE)) delete_connection(c, FALSE);}voiddelete_every_connection(void){ while (connections != NULL) delete_connection(connections, TRUE);}voidrelease_dead_interfaces(void){ struct host_pair *hp; for (hp = host_pairs; hp != NULL; hp = hp->next) { struct connection **pp , *p; for (pp = &hp->connections; (p = *pp) != NULL; ) { if (p->interface->change == IFN_DELETE) { /* this connection's interface is going away */ enum connection_kind k = p->kind; release_connection(p, TRUE); if (k <= CK_PERMANENT) { /* The connection should have survived release: * move it to the unoriented_connections list. */ passert(p == *pp); p->interface = NULL; *pp = p->hp_next; /* advance *pp */ p->host_pair = NULL; p->hp_next = unoriented_connections; unoriented_connections = p; } else { /* The connection should have vanished, * but the previous connection remains. */ passert(p != *pp); } } else { pp = &p->hp_next; /* advance pp */ } } }}/* adjust orientations of connections to reflect newly added interfaces */voidcheck_orientations(void){ /* try to orient all the unoriented connections */ { struct connection *c = unoriented_connections; unoriented_connections = NULL; while (c != NULL) { struct connection *nxt = c->hp_next; (void)orient(c); connect_to_host_pair(c); c = nxt; } } /* Check that no oriented connection has become double-oriented. * In other words, the far side must not match one of our new interfaces. */ { struct iface *i; for (i = interfaces; i != NULL; i = i->next) { if (i->change == IFN_ADD) { struct host_pair *hp; for (hp = host_pairs; hp != NULL; hp = hp->next) { if (sameaddr(&hp->him.addr, &i->addr) && (!no_klips || hp->him.port == pluto_port)) { /* bad news: the whole chain of connections * hanging off this host pair has both sides * matching an interface. * We'll get rid of them, using orient and * connect_to_host_pair. But we'll be lazy * and not ditch the host_pair itself (the * cost of leaving it is slight and cannot * be induced by a foe). */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -