⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 state.c

📁 ipsec vpn
💻 C
📖 第 1 页 / 共 2 页
字号:
/* routines for state objects * Copyright (C) 1997 Angelos D. Keromytis. * Copyright (C) 1998-2001  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: state.c,v 1.151.4.5 2006/03/20 13:40:23 paul Exp $ */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <fcntl.h>#include <sys/queue.h>#include <openswan.h>#include "constants.h"#include "defs.h"#include "id.h"#include "x509.h"#include "pgp.h"#include "certs.h"#include "smartcard.h"#ifdef XAUTH_USEPAM#include <security/pam_appl.h>#endif#include "connections.h"	/* needs id.h */#include "state.h"#include "kernel.h"	/* needs connections.h */#include "log.h"#include "packet.h"	/* so we can calculate sizeof(struct isakmp_hdr) */#include "keys.h"	/* for free_public_key */#include "rnd.h"#include "timer.h"#include "whack.h"#include "demux.h"	/* needs packet.h */#include "pending.h"#include "ipsec_doi.h"	/* needs demux.h and state.h */#include "sha1.h"#include "md5.h"#include "crypto.h" /* requires sha1.h and md5.h *//* * Global variables: had to go somewhere, might as well be this file. */u_int16_t pluto_port = IKE_UDP_PORT;	/* Pluto's port *//* * This file has the functions that handle the * state hash table and the Message ID list. *//* Message-IDs * * A Message ID is contained in each IKE message header. * For Phase 1 exchanges (Main and Aggressive), it will be zero. * For other exchanges, which must be under the protection of an * ISAKMP SA, the Message ID must be unique within that ISAKMP SA. * Effectively, this labels the message as belonging to a particular * exchange. * BTW, we feel this uniqueness allows rekeying to be somewhat simpler * than specified by draft-jenkins-ipsec-rekeying-06.txt. * * A MessageID is a 32 bit unsigned number.  We represent the value * internally in network order -- they are just blobs to us. * They are unsigned numbers to make hashing and comparing easy. * * The following mechanism is used to allocate message IDs.  This * requires that we keep track of which numbers have already been used * so that we don't allocate one in use. */struct msgid_list{    msgid_t               msgid; /* network order */    struct msgid_list     *next;};boolreserve_msgid(struct state *isakmp_sa, msgid_t msgid){    struct msgid_list *p;    passert(msgid != MAINMODE_MSGID);    passert(IS_ISAKMP_ENCRYPTED(isakmp_sa->st_state));    for (p = isakmp_sa->st_used_msgids; p != NULL; p = p->next)	if (p->msgid == msgid)	    return FALSE;    p = alloc_thing(struct msgid_list, "msgid");    p->msgid = msgid;    p->next = isakmp_sa->st_used_msgids;    isakmp_sa->st_used_msgids = p;    return TRUE;}msgid_tgenerate_msgid(struct state *isakmp_sa){    int timeout = 100;	/* only try so hard for unique msgid */    msgid_t msgid;    passert(IS_ISAKMP_ENCRYPTED(isakmp_sa->st_state));    for (;;)    {	get_rnd_bytes((void *) &msgid, sizeof(msgid));	if (msgid != 0 && reserve_msgid(isakmp_sa, msgid))	    break;	if (--timeout == 0)	{	    openswan_log("gave up looking for unique msgid; using 0x%08lx"		, (unsigned long) msgid);	    break;	}    }    return msgid;}/* state table functions */#define STATE_TABLE_SIZE 32static struct state *statetable[STATE_TABLE_SIZE];static struct state **state_hash(const u_char *icookie, const u_char *rcookie, const ip_address *peer){    u_int i = 0, j;    const unsigned char *byte_ptr;    size_t length = addrbytesptr(peer, &byte_ptr);    DBG(DBG_RAW | DBG_CONTROL,	DBG_dump("ICOOKIE:", icookie, COOKIE_SIZE);	DBG_dump("RCOOKIE:", rcookie, COOKIE_SIZE);	DBG_dump("peer:", byte_ptr, length));    /* XXX the following hash is pretty pathetic */    for (j = 0; j < COOKIE_SIZE; j++)	i = i * 407 + icookie[j] + rcookie[j];    for (j = 0; j < length; j++)	i = i * 613 + byte_ptr[j];    i = i % STATE_TABLE_SIZE;    DBG(DBG_CONTROL, DBG_log("state hash entry %d", i));    return &statetable[i];}/* Get a state object. * Caller must schedule an event for this object so that it doesn't leak. * Caller must insert_state(). */struct state *new_state(void){    static const struct state blank_state;	/* initialized all to zero & NULL */    static so_serial_t next_so = SOS_FIRST;    struct state *st;    st = clone_thing(blank_state, "struct state in new_state()");    st->st_serialno = next_so++;    passert(next_so > SOS_FIRST);	/* overflow can't happen! */#ifdef XAUTH    passert(st->st_oakley.xauth == 0);    passert(st->st_xauth_username == NULL);#endif        st->st_whack_sock = NULL_FD;        anyaddr(AF_INET, &st->hidden_variables.st_nat_oa);    anyaddr(AF_INET, &st->hidden_variables.st_natd);    DBG(DBG_CONTROL, DBG_log("creating state object #%lu at %p",	st->st_serialno, (void *) st));    return st;}/* * Initialize the state table (and mask*). */voidinit_states(void){    int i;    for (i = 0; i < STATE_TABLE_SIZE; i++)	statetable[i] = (struct state *) NULL;}/* Find the state object with this serial number. * This allows state object references that don't turn into dangerous * dangling pointers: reference a state by its serial number. * Returns NULL if there is no such state. * If this turns out to be a significant CPU hog, it could be * improved to use a hash table rather than sequential seartch. */struct state *state_with_serialno(so_serial_t sn){    if (sn >= SOS_FIRST)    {	struct state *st;	int i;	for (i = 0; i < STATE_TABLE_SIZE; i++)	    for (st = statetable[i]; st != NULL; st = st->st_hashchain_next)		if (st->st_serialno == sn)		    return st;    }    return NULL;}/* Insert a state object in the hash table. The object is inserted * at the begining of list. * Needs cookies, connection, and msgid. */voidinsert_state(struct state *st){    struct state **p = state_hash(st->st_icookie, st->st_rcookie	, &st->st_connection->spd.that.host_addr);    passert(st->st_hashchain_prev == NULL && st->st_hashchain_next == NULL);    if (*p != NULL)    {	passert((*p)->st_hashchain_prev == NULL);	(*p)->st_hashchain_prev = st;    }    st->st_hashchain_next = *p;    *p = st;    /* Ensure that somebody is in charge of killing this state:     * if no event is scheduled for it, schedule one to discard the state.     * If nothing goes wrong, this event will be replaced by     * a more appropriate one.     */    if (st->st_event == NULL)	event_schedule(EVENT_SO_DISCARD, 0, st);}/* unlink a state object from the hash table, but don't free it */voidunhash_state(struct state *st){    /* unlink from forward chain */    struct state **p = st->st_hashchain_prev == NULL	? state_hash(st->st_icookie, st->st_rcookie		     , &st->st_connection->spd.that.host_addr)	: &st->st_hashchain_prev->st_hashchain_next;    /* unlink from forward chain */    passert(*p == st);    *p = st->st_hashchain_next;    /* unlink from backward chain */    if (st->st_hashchain_next != NULL)    {	passert(st->st_hashchain_next->st_hashchain_prev == st);	st->st_hashchain_next->st_hashchain_prev = st->st_hashchain_prev;    }    st->st_hashchain_next = st->st_hashchain_prev = NULL;}/* Free the Whack socket file descriptor. * This has the side effect of telling Whack that we're done. */voidrelease_whack(struct state *st){    close_any(st->st_whack_sock);}/* delete a state object */voiddelete_state(struct state *st){    struct connection *const c = st->st_connection;    struct state *old_cur_state = cur_state == st? NULL : cur_state;    DBG(DBG_CONTROL, DBG_log("deleting state #%lu", st->st_serialno));    set_cur_state(st);    /* If DPD is enabled on this state object, clear any pending events */    if(st->st_dpd_event != NULL)            delete_dpd_event(st);    /* if there is a suspended state transition, disconnect us */    if (st != NULL && st->st_suspended_md != NULL)    {	passert(st->st_suspended_md->st == st);	st->st_suspended_md->st = NULL;    }    /* tell the other side of any IPSEC SAs that are going down */    if (IS_IPSEC_SA_ESTABLISHED(st->st_state)    || IS_ISAKMP_SA_ESTABLISHED(st->st_state))	send_delete(st);    delete_event(st);	/* delete any pending timer event */    /* Ditch anything pending on ISAKMP SA being established.     * Note: this must be done before the unhash_state to prevent     * flush_pending_by_state inadvertently and prematurely     * deleting our connection.     */    flush_pending_by_state(st);    /* if there is anything in the cryptographic queue, then remove this     * state from it.     */    delete_cryptographic_continuation(st);    /* effectively, this deletes any ISAKMP SA that this state represents */    unhash_state(st);    /* tell kernel to delete any IPSEC SA     * ??? we ought to tell peer to delete IPSEC SAs     */    if (IS_IPSEC_SA_ESTABLISHED(st->st_state))	delete_ipsec_sa(st, FALSE);    else if (IS_ONLY_INBOUND_IPSEC_SA_ESTABLISHED(st->st_state))	delete_ipsec_sa(st, TRUE);    if (c->newest_ipsec_sa == st->st_serialno)	c->newest_ipsec_sa = SOS_NOBODY;    if (c->newest_isakmp_sa == st->st_serialno)	c->newest_isakmp_sa = SOS_NOBODY;    st->st_connection = NULL;	/* we might be about to free it */    cur_state = old_cur_state;	/* without st_connection, st isn't complete */    connection_discard(c);    release_whack(st);    /* from here on we are just freeing RAM */    {	struct msgid_list *p = st->st_used_msgids;	while (p != NULL)	{	    struct msgid_list *q = p;	    p = p->next;	    pfree(q);	}    }    unreference_key(&st->st_peer_pubkey);    if (st->st_sec_in_use) {	mpz_clear(&(st->st_sec));	pfreeany(st->st_sec_chunk.ptr);    }    pfreeany(st->st_tpacket.ptr);    pfreeany(st->st_rpacket.ptr);    pfreeany(st->st_p1isa.ptr);    pfreeany(st->st_gi.ptr);    pfreeany(st->st_gr.ptr);    pfreeany(st->st_shared.ptr);    pfreeany(st->st_ni.ptr);    pfreeany(st->st_nr.ptr);    pfreeany(st->st_skeyid.ptr);    pfreeany(st->st_skeyid_d.ptr);    pfreeany(st->st_skeyid_a.ptr);    pfreeany(st->st_skeyid_e.ptr);    pfreeany(st->st_enc_key.ptr);    pfreeany(st->st_ah.our_keymat);    pfreeany(st->st_ah.peer_keymat);    pfreeany(st->st_esp.our_keymat);    pfreeany(st->st_esp.peer_keymat);    pfreeany(st->st_xauth_username);    pfree(st);}/* * Is a connection in use by some state? */boolstates_use_connection(struct connection *c){    /* are there any states still using it? */    struct state *st = NULL;    int i;    for (i = 0; st == NULL && i < STATE_TABLE_SIZE; i++)	for (st = statetable[i]; st != NULL; st = st->st_hashchain_next)	    if (st->st_connection == c)		return TRUE;    return FALSE;}/* * delete all states that were created for a given connection. * if relations == TRUE, then also delete states that share * the same phase 1 SA. */voiddelete_states_by_connection(struct connection *c, bool relations){    int pass;    /* this kludge avoids an n^2 algorithm */    enum connection_kind ck = c->kind;    struct spd_route *sr;    /* save this connection's isakmp SA, since it will get set to later SOS_NOBODY */    so_serial_t parent_sa = c->newest_isakmp_sa;    if (ck == CK_INSTANCE)	c->kind = CK_GOING_AWAY;    /* We take two passes so that we delete any ISAKMP SAs last.     * This allows Delete Notifications to be sent.     * ?? We could probably double the performance by caching any     * ISAKMP SA states found in the first pass, avoiding a second.     */    for (pass = 0; pass != 2; pass++)    {	int i;	/* For each hash chain... */	for (i = 0; i < STATE_TABLE_SIZE; i++)	{	    struct state *st;	    /* For each state in the hash chain... */	    for (st = statetable[i]; st != NULL; )	    {		struct state *this = st;		st = st->st_hashchain_next;	/* before this is deleted */                if ((this->st_connection == c			|| (relations && parent_sa != SOS_NOBODY 			&& this->st_clonedfrom == parent_sa))			&& (pass == 1 || !IS_ISAKMP_SA_ESTABLISHED(this->st_state)))                {                    struct state *old_cur_state                        = cur_state == this? NULL : cur_state;#ifdef DEBUG		    lset_t old_cur_debugging = cur_debugging;#endif		    set_cur_state(this);		    openswan_log("deleting state (%s)"			, enum_show(&state_names, this->st_state));		    if(this->st_event != NULL) delete_event(this);		    delete_state(this);		    cur_state = old_cur_state;#ifdef DEBUG		    set_debugging(old_cur_debugging);#endif		}	    }	}    } /*  Seems to dump here because 1 of the states is NULL.  Removing the Assert     makes things work.  We should fix this eventually.    passert(c->newest_ipsec_sa == SOS_NOBODY	    && c->newest_isakmp_sa == SOS_NOBODY); */    sr = &c->spd;    while (sr != NULL)    {	passert(sr->eroute_owner == SOS_NOBODY);	passert(sr->routing != RT_ROUTED_TUNNEL);	sr = sr->next;    }    if (ck == CK_INSTANCE)    {	c->kind = ck;	delete_connection(c, relations);    }}/* Walk through the state table, and delete each state whose phase 1 (IKE) * peer is among those given. */voiddelete_states_by_peer(ip_address *peer){    char peerstr[ADDRTOT_BUF];    int i, ph1;    addrtot(peer, 0, peerstr, sizeof(peerstr));    whack_log(RC_COMMENT, "restarting peer %s\n", peerstr);    /* first restart the phase1s */    for(ph1=0; ph1 < 2; ph1++) {	/* For each hash chain... */	for (i = 0; i < STATE_TABLE_SIZE; i++) {	    struct state *st;			    /* For each state in the hash chain... */	    for (st = statetable[i]; st != NULL; ) {		struct state *this = st;		struct connection *c = this->st_connection;		char ra[ADDRTOT_BUF];				st = st->st_hashchain_next;	/* before this is deleted */				addrtot(&st->st_remoteaddr, 0, ra, sizeof(ra));		DBG_log("comparing %s to %s\n", ra, peerstr);		if(sameaddr(&st->st_remoteaddr, peer)) {		    if(ph1==0 && IS_PHASE1(st->st_state)) {						whack_log(RC_COMMENT				  , "peer %s for connection %s crashed, replacing"				  , peerstr				  , c->name);			ipsecdoi_replace(st, 1);		    } else {			delete_event(st);			event_schedule(EVENT_SA_REPLACE, 0, st);		    }		}	    }	}    }}/* Duplicate a Phase 1 state object, to create a Phase 2 object. * Caller must schedule an event for this object so that it doesn't leak. * Caller must insert_state(). */struct state *duplicate_state(struct state *st){    struct state *nst;    DBG(DBG_CONTROL, DBG_log("duplicating state object #%lu",	st->st_serialno));    /* record use of the Phase 1 state */    st->st_outbound_count++;    st->st_outbound_time = now();    nst = new_state();        memcpy(nst->st_icookie, st->st_icookie, COOKIE_SIZE);    memcpy(nst->st_rcookie, st->st_rcookie, COOKIE_SIZE);    nst->st_connection = st->st_connection;    nst->st_doi = st->st_doi;    nst->st_situation = st->st_situation;    nst->quirks = st->quirks;    nst->hidden_variables = st->hidden_variables;    if(st->st_xauth_username) {	nst->st_xauth_username = clone_str(st->st_xauth_username					   , "xauth username");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -