[BACK]Return to exchange.c CVS log [TXT][DIR] Up to [local] / src / sbin / isakmpd

File: [local] / src / sbin / isakmpd / exchange.c (download)

Revision 1.45, Tue Apr 24 07:27:36 2001 UTC (23 years, 1 month ago) by niklas
Branch: MAIN
CVS Tags: OPENBSD_2_9_BASE, OPENBSD_2_9
Changes since 1.44: +4 -8 lines

Correct SA refcounting.  Fixes a bug where isakmpd could die when a peer was
discovered to have rebooted, and old now invalid SAs had to be garbage-
collected.

/*	$OpenBSD: exchange.c,v 1.45 2001/04/24 07:27:36 niklas Exp $	*/
/*	$EOM: exchange.c,v 1.143 2000/12/04 00:02:25 angelos Exp $	*/

/*
 * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist.  All rights reserved.
 * Copyright (c) 1999, 2001 Angelos D. Keromytis.  All rights reserved.
 * Copyright (c) 1999, 2000 Håkan Olsson.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Ericsson Radio Systems.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * This code was written under funding by Ericsson Radio Systems.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>

#include "sysdep.h"

#include "cert.h"
#include "conf.h"
#include "connection.h"
#include "constants.h"
#include "cookie.h"
#include "crypto.h"
#include "doi.h"
#include "exchange.h"
#include "ipsec_num.h"
#include "isakmp.h"
#include "libcrypto.h"
#include "log.h"
#include "message.h"
#include "timer.h"
#include "transport.h"
#include "ipsec.h"
#include "sa.h"
#include "util.h"
#ifdef USE_X509
#include "x509.h"
#endif

/* Initial number of bits from the cookies used as hash.  */
#define INITIAL_BUCKET_BITS 6

/*
 * Don't try to use more bits than this as a hash.
 * We only XOR 16 bits so going above that means changing the code below
 * too.
 */
#define MAX_BUCKET_BITS 16

static void exchange_dump (char *, struct exchange *);
static void exchange_free_aux (void *);

static LIST_HEAD (exchange_list, exchange) *exchange_tab;

/* Works both as a maximum index and a mask.  */
static int bucket_mask;

/*
 * Validation scripts used to test messages for correct content of
 * payloads depending on the exchange type.
 */
int16_t script_base[] = {
  ISAKMP_PAYLOAD_SA,		/* Initiator -> responder.  */
  ISAKMP_PAYLOAD_NONCE,
  EXCHANGE_SCRIPT_SWITCH,
  ISAKMP_PAYLOAD_SA,		/* Responder -> initiator.  */
  ISAKMP_PAYLOAD_NONCE,
  EXCHANGE_SCRIPT_SWITCH,
  ISAKMP_PAYLOAD_KEY_EXCH,	/* Initiator -> responder.  */
  ISAKMP_PAYLOAD_ID,
  EXCHANGE_SCRIPT_AUTH,
  EXCHANGE_SCRIPT_SWITCH,
  ISAKMP_PAYLOAD_KEY_EXCH,	/* Responder -> initiator.  */
  ISAKMP_PAYLOAD_ID,
  EXCHANGE_SCRIPT_AUTH,
  EXCHANGE_SCRIPT_END
};

int16_t script_identity_protection[] = {
  ISAKMP_PAYLOAD_SA,		/* Initiator -> responder.  */
  EXCHANGE_SCRIPT_SWITCH,
  ISAKMP_PAYLOAD_SA,		/* Responder -> initiator.  */
  EXCHANGE_SCRIPT_SWITCH,
  ISAKMP_PAYLOAD_KEY_EXCH,	/* Initiator -> responder.  */
  ISAKMP_PAYLOAD_NONCE,
  EXCHANGE_SCRIPT_SWITCH,
  ISAKMP_PAYLOAD_KEY_EXCH,	/* Responder -> initiator.  */
  ISAKMP_PAYLOAD_NONCE,
  EXCHANGE_SCRIPT_SWITCH,
  ISAKMP_PAYLOAD_ID,		/* Initiator -> responder.  */
  EXCHANGE_SCRIPT_AUTH,
  EXCHANGE_SCRIPT_SWITCH,
  ISAKMP_PAYLOAD_ID,		/* Responder -> initiator.  */
  EXCHANGE_SCRIPT_AUTH,
  EXCHANGE_SCRIPT_END
};

int16_t script_authentication_only[] = {
  ISAKMP_PAYLOAD_SA,		/* Initiator -> responder.  */
  ISAKMP_PAYLOAD_NONCE,
  EXCHANGE_SCRIPT_SWITCH,
  ISAKMP_PAYLOAD_SA,		/* Responder -> initiator.  */
  ISAKMP_PAYLOAD_NONCE,
  ISAKMP_PAYLOAD_ID,
  EXCHANGE_SCRIPT_AUTH,
  EXCHANGE_SCRIPT_SWITCH,
  ISAKMP_PAYLOAD_ID,		/* Initiator -> responder.  */
  EXCHANGE_SCRIPT_AUTH,
  EXCHANGE_SCRIPT_END
};

#ifdef USE_AGGRESSIVE
int16_t script_aggressive[] = {
  ISAKMP_PAYLOAD_SA,		/* Initiator -> responder.  */
  ISAKMP_PAYLOAD_KEY_EXCH,
  ISAKMP_PAYLOAD_NONCE,
  ISAKMP_PAYLOAD_ID,
  EXCHANGE_SCRIPT_SWITCH,
  ISAKMP_PAYLOAD_SA,		/* Responder -> initiator.  */
  ISAKMP_PAYLOAD_KEY_EXCH,
  ISAKMP_PAYLOAD_NONCE,
  ISAKMP_PAYLOAD_ID,
  EXCHANGE_SCRIPT_AUTH,
  EXCHANGE_SCRIPT_SWITCH,
  EXCHANGE_SCRIPT_AUTH,		/* Initiator -> responder.  */
  EXCHANGE_SCRIPT_END
};
#endif /* USE_AGGRESSIVE */

int16_t script_informational[] = {
  EXCHANGE_SCRIPT_INFO,		/* Initiator -> responder.  */
  EXCHANGE_SCRIPT_END
};

/*
 * Check what exchange SA is negotiated with and return a suitable validation
 * script.
 */
u_int16_t *
exchange_script (struct exchange *exchange)
{
  switch (exchange->type)
    {
    case ISAKMP_EXCH_BASE:
      return script_base;
    case ISAKMP_EXCH_ID_PROT:
      return script_identity_protection;
    case ISAKMP_EXCH_AUTH_ONLY:
      return script_authentication_only;
#ifdef USE_AGGRESSIVE
    case ISAKMP_EXCH_AGGRESSIVE:
      return script_aggressive;
#endif
    case ISAKMP_EXCH_INFO:
      return script_informational;
    default:
      if (exchange->type >= ISAKMP_EXCH_DOI_MIN
	  && exchange->type <= ISAKMP_EXCH_DOI_MAX)
	return exchange->doi->exchange_script (exchange->type);
    }
  return 0;
}

/*
 * Validate the message MSG's contents wrt what payloads the exchange type
 * requires at this point in the dialogoue.  Return -1 if the validation fails,
 * 0 if it succeeds and the script is not finished and 1 if it's ready.
 */
static int
exchange_validate (struct message *msg)
{
  struct exchange *exchange = msg->exchange;
  int16_t *pc = exchange->exch_pc;

  while (*pc != EXCHANGE_SCRIPT_END && *pc != EXCHANGE_SCRIPT_SWITCH)
    {
      LOG_DBG ((LOG_EXCHANGE, 90, 
		"exchange_validate: checking for required %s",
		*pc >= ISAKMP_PAYLOAD_NONE
		? constant_name (isakmp_payload_cst, *pc)
		: constant_name (exchange_script_cst, *pc)));

      /* Check for existence of the required payloads.  */
      if ((*pc > 0 && !TAILQ_FIRST (&msg->payload[*pc]))
	  || (*pc == EXCHANGE_SCRIPT_AUTH
	      && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH])
	      && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SIG]))
	  || (*pc == EXCHANGE_SCRIPT_INFO
	      && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_NOTIFY])
	      && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_DELETE])))
	{
	  /* Missing payload.  */
	  LOG_DBG ((LOG_MESSAGE, 70,
		    "exchange_validate: msg %p requires missing %s", msg,
		    *pc >= ISAKMP_PAYLOAD_NONE
		    ? constant_name (isakmp_payload_cst, *pc)
		    : constant_name (exchange_script_cst, *pc)));
	  return -1;
	}
      pc++;
    }
  if (*pc == EXCHANGE_SCRIPT_END)
    /* Cleanup.  */
    return 1;

  return 0;
}

/*
 * Run the exchange script from a point given by the "program counter"
 * upto either the script's end or a transmittal of a message.  If we are
 * at the point of a reception of a message, that message should be handed
 * in here in the MSG argument.  Otherwise we are the initiator and should
 * expect MSG to be a half-cooked message without payloads.
 */
void
exchange_run (struct message *msg)
{
  int i, done = 0;
  struct exchange *exchange = msg->exchange;
  struct doi *doi = exchange->doi;
  int (*handler) (struct message *)
    = exchange->initiator ? doi->initiator : doi->responder;
  struct payload *payload;

  while (!done)
    {
      /*
       * It's our turn if we're either the initiator on an even step,
       * or the responder on an odd step of the dialogue.
       */
      if (exchange->initiator ^ (exchange->step % 2))
	{
	  done = 1;
	  if (exchange->step)
	    msg = message_alloc_reply (msg);
	  message_setup_header (msg, exchange->type, 0, exchange->message_id);
	  if (handler (msg))
	    {
	      /*
	       * This can happen when transient starvation of memory occurs.
	       * XXX The peer's retransmit ought to kick-start this exchange
	       * again.  If he's stopped retransmitting he's likely dropped
	       * the SA at his side so we need to do that too, i.e.
	       * implement automatic SA teardown after a certain amount
	       * of inactivity.
	       */
	      log_print ("exchange_run: doi->%s (%p) failed",
			 exchange->initiator ? "initiator" : "responder", msg);
	      message_free (msg);
	      return;
	    }

	  switch (exchange_validate (msg))
	    {
	    case 1:
	      /*
	       * The last message of a multi-message exchange should
	       * not be retransmitted other than "on-demand", i.e. if we
	       * see retransmits of the last message of the peer
	       * later.
	       */
	      msg->flags |= MSG_LAST;
	      if (exchange->step > 0)
		{
		  if (exchange->last_sent)
		    message_free (exchange->last_sent);
		  exchange->last_sent = msg;
		}

	      /*
	       * After we physically have sent our last message we need to
	       * do SA-specific finalization, like telling our application
	       * the SA is ready to be used, or issuing a CONNECTED notify
	       * if we set the COMMIT bit.
	       */
	      message_register_post_send (msg, exchange_finalize);

	      /* Fallthrough.  */

	    case 0:
	      /* XXX error handling.  */
	      message_send (msg);
	      break;

	    default:
	      log_print ("exchange_run: exchange_validate failed, DOI error");
	      exchange_free (exchange);
	      message_free (msg);
	      return;
	    }
	}      
      else
	{
	  done = exchange_validate (msg);
	  switch (done)
	    {
	    case 0:
	    case 1:
	      /* Feed the message to the DOI.  */
	      if (handler (msg))
		{
		  /*
		   * Trust the peer to retransmit.
		   * XXX We have to implement SA aging with automatic teardown.
		   */
		  message_free (msg);
		  return;
		}

	      /*
	       * Go over the yet unhandled payloads and feed them to DOI
	       * for handling.
	       */
	      for (i = ISAKMP_PAYLOAD_SA; i < ISAKMP_PAYLOAD_RESERVED_MIN; i++)
		if (i != ISAKMP_PAYLOAD_PROPOSAL
		    && i != ISAKMP_PAYLOAD_TRANSFORM)
		  for (payload = TAILQ_FIRST (&msg->payload[i]); payload;
		       payload = TAILQ_NEXT (payload, link))
		    if ((payload->flags & PL_MARK) == 0)
		      if (!doi->handle_leftover_payload
			  || doi->handle_leftover_payload (msg, i, payload))
			LOG_DBG ((LOG_EXCHANGE, 10, 
				  "exchange_run: unexpected payload %s",
				  constant_name (isakmp_payload_cst, i)));

	      /*
	       * We have advanced the state.  If we have been processing an
	       * incoming message, record that message as the one to do
	       * duplication tests against.
	       */
	      if (exchange->last_received)
		message_free (exchange->last_received);
	      exchange->last_received = msg;
	      if (exchange->flags & EXCHANGE_FLAG_ENCRYPT)
		crypto_update_iv (exchange->keystate);

	      if (done)
		{
		  exchange_finalize (msg);
		  return;
		}
	      break;

	    case -1:
	      log_print ("exchange_run: exchange_validate failed");
	      /* XXX Is this the best error notification type?  */
	      message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1);
	      return;
	    }
	}

      LOG_DBG ((LOG_EXCHANGE, 40, 
		"exchange_run: exchange %p finished step %d, advancing...",
		exchange, exchange->step));
      exchange->step++;
      while (*exchange->exch_pc != EXCHANGE_SCRIPT_SWITCH
	     && *exchange->exch_pc != EXCHANGE_SCRIPT_END)
	exchange->exch_pc++;
      exchange->exch_pc++;
    }
}

void
exchange_init ()
{
  int i;

  bucket_mask = (1 << INITIAL_BUCKET_BITS) - 1;
  exchange_tab = malloc ((bucket_mask + 1) * sizeof (struct exchange_list));
  if (!exchange_tab)
    log_fatal ("exchange_init: out of memory");
  for (i = 0; i <= bucket_mask; i++)
    {
      LIST_INIT (&exchange_tab[i]);
    }
 
}

void
exchange_resize ()
{
  int new_mask = (bucket_mask + 1) * 2 - 1;
  int i;
  struct exchange_list *new_tab;

  new_tab
    = realloc (exchange_tab, (new_mask + 1) * sizeof (struct exchange_list));
  if (!new_tab)
    return;
  for (i = bucket_mask + 1; i <= new_mask; i++)
    {
      LIST_INIT (&new_tab[i]);
    }
  bucket_mask = new_mask;
  /* XXX Rehash existing entries.  */
}

/* Lookup a phase 1 exchange out of just the initiator cookie.  */
struct exchange *
exchange_lookup_from_icookie (u_int8_t *cookie)
{
  int i;
  struct exchange *exchange;

  for (i = 0; i <= bucket_mask; i++)
    for (exchange = LIST_FIRST (&exchange_tab[i]); exchange;
	 exchange = LIST_NEXT (exchange, link))
      if (memcmp (exchange->cookies, cookie, ISAKMP_HDR_ICOOKIE_LEN) == 0
	  && exchange->phase == 1)
	return exchange;
  return 0;
}

/* Lookup an exchange out of the name and phase.  */
struct exchange *
exchange_lookup_by_name (char *name, int phase)
{
  int i;
  struct exchange *exchange;

  /* If we search for nothing, we will find nothing.  */
  if (!name)
    return 0;

  for (i = 0; i <= bucket_mask; i++)
    for (exchange = LIST_FIRST (&exchange_tab[i]); exchange;
	 exchange = LIST_NEXT (exchange, link))
      {
	LOG_DBG ((LOG_EXCHANGE, 90,
		  "exchange_lookup_by_name: %s == %s && %d == %d?", name,
		  exchange->name ? exchange->name : "<unnamed>", phase,
		  exchange->phase));

	/* 
	 * Match by name, but don't select finished exchanges, i.e
	 * where MSG_LAST are set in last_sent msg.
	 */
	if (exchange->name && strcasecmp (exchange->name, name) == 0
	    && exchange->phase == phase
	    && (!exchange->last_sent
		|| (exchange->last_sent->flags & MSG_LAST) == 0))
	  return exchange;
      }
  return 0;
}

/* Lookup an exchange out of the name, phase and step > 1.  */
struct exchange *
exchange_lookup_active (char *name, int phase)
{
  int i;
  struct exchange *exchange;

  /* XXX Almost identical to exchange_lookup_by_name.  */

  if (!name)
    return 0;

  for (i = 0; i <= bucket_mask; i++)
    for (exchange = LIST_FIRST (&exchange_tab[i]); exchange;
	 exchange = LIST_NEXT (exchange, link))
      {
	LOG_DBG ((LOG_EXCHANGE, 90,
		  "exchange_lookup_active: %s == %s && %d == %d?",
		  name, exchange->name ? exchange->name : "<unnamed>", phase,
		  exchange->phase));
	if (exchange->name && strcasecmp (exchange->name, name) == 0
	    && exchange->phase == phase)
	  {
	    if (exchange->step > 1)
	      return exchange;
	    else
	      LOG_DBG ((LOG_EXCHANGE, 80, 
			"exchange_lookup_active: avoided early (pre-step 1) "
			"exchange %p", exchange));
	  }
      }
  return 0;
}

static void
exchange_enter (struct exchange *exchange)
{
  u_int16_t bucket = 0;
  int i;
  u_int8_t *cp;

  /* XXX We might resize if we are crossing a certain threshold */

  for (i = 0; i < ISAKMP_HDR_COOKIES_LEN; i += 2)
    {
      cp = exchange->cookies + i;
      /* Doing it this way avoids alignment problems.  */
      bucket ^= cp[0] | cp[1] << 8;
    }
  for (i = 0; i < ISAKMP_HDR_MESSAGE_ID_LEN; i += 2)
    {
      cp = exchange->message_id + i;
      /* Doing it this way avoids alignment problems.  */
      bucket ^= cp[0] | cp[1] << 8;
    }
  bucket &= bucket_mask;
  LIST_INSERT_HEAD (&exchange_tab[bucket], exchange, link);
}

/*
 * Lookup the exchange given by the header fields MSG.  PHASE2 is false when
 * looking for phase 1 exchanges and true otherwise.
 */
struct exchange *
exchange_lookup (u_int8_t *msg, int phase2)
{
  u_int16_t bucket = 0;
  int i;
  struct exchange *exchange;
  u_int8_t *cp;

  /*
   * We use the cookies to get bits to use as an index into exchange_tab, as at
   * least one (our cookie) is a good hash, xoring all the bits, 16 at a
   * time, and then masking, should do.  Doing it this way means we can
   * validate cookies very fast thus delimiting the effects of "Denial of
   * service"-attacks using packet flooding.
   */
  for (i = 0; i < ISAKMP_HDR_COOKIES_LEN; i += 2)
    {
      cp = msg + ISAKMP_HDR_COOKIES_OFF + i;
      /* Doing it this way avoids alignment problems.  */
      bucket ^= cp[0] | cp[1] << 8;
    }
  if (phase2)
    for (i = 0; i < ISAKMP_HDR_MESSAGE_ID_LEN; i += 2)
      {
	cp = msg + ISAKMP_HDR_MESSAGE_ID_OFF + i;
	/* Doing it this way avoids alignment problems.  */
	bucket ^= cp[0] | cp[1] << 8;
      }
  bucket &= bucket_mask;
  for (exchange = LIST_FIRST (&exchange_tab[bucket]);
       exchange && (memcmp (msg + ISAKMP_HDR_COOKIES_OFF, exchange->cookies,
			    ISAKMP_HDR_COOKIES_LEN) != 0
		    || (phase2 && memcmp (msg + ISAKMP_HDR_MESSAGE_ID_OFF,
					  exchange->message_id,
					  ISAKMP_HDR_MESSAGE_ID_LEN) != 0)
		    || (!phase2 && !zero_test (msg + ISAKMP_HDR_MESSAGE_ID_OFF,
					       ISAKMP_HDR_MESSAGE_ID_LEN)));
       exchange = LIST_NEXT (exchange, link))
    ;

  return exchange;
}

/*
 * Create a phase PHASE exchange where INITIATOR denotes our role.  DOI
 * is the domain of interpretation identifier and TYPE tells what exchange
 * type to use per either the DOI document or the ISAKMP spec proper.
 * NSA tells how many SAs we should pre-allocate, and should be zero
 * when we have the responder role.
 */
static struct exchange *
exchange_create (int phase, int initiator, int doi, int type)
{
  struct exchange *exchange;
  struct timeval expiration;
  int delta;

  /*
   * We want the exchange zeroed for exchange_free to be able to find
   * out what fields have been filled-in.
   */
  exchange = calloc (1, sizeof *exchange);
  if (!exchange)
    {
      log_error ("exchange_create: calloc (1, %d) failed", sizeof *exchange);
      return 0;
    }
  exchange->phase = phase;
  exchange->step = 0;
  exchange->initiator = initiator;
  memset (exchange->cookies, 0, ISAKMP_HDR_COOKIES_LEN);
  memset (exchange->message_id, 0, ISAKMP_HDR_MESSAGE_ID_LEN);
  exchange->doi = doi_lookup (doi);
  exchange->type = type;
  exchange->policy_id = -1;
  exchange->exch_pc = exchange_script (exchange);
  exchange->last_sent = exchange->last_received = 0;
  TAILQ_INIT (&exchange->sa_list);
  TAILQ_INIT (&exchange->aca_list);

  /* Allocate the DOI-specific structure and initialize it to zeroes.  */
  if (exchange->doi->exchange_size)
    {
      exchange->data = calloc (1, exchange->doi->exchange_size);
      if (!exchange->data)
	{
	  log_error ("exchange_create: calloc (1, %d) failed",
		     exchange->doi->exchange_size);
	  exchange_free (exchange);
	  return 0;
	}
    }

  gettimeofday (&expiration, 0);
  delta = conf_get_num ("General", "Exchange-max-time", EXCHANGE_MAX_TIME);
  expiration.tv_sec += delta;
  exchange->death = timer_add_event ("exchange_free_aux", exchange_free_aux,
				     exchange, &expiration);
  if (!exchange->death)
    {
      /* If we don't give up we might start leaking...  */
      exchange_free_aux (exchange);
      return 0;
    }

  return exchange;
}

struct exchange_finalization_node
{
  void (*first) (struct exchange *, void *, int);
  void *first_arg;
  void (*second) (struct exchange *, void *, int);
  void *second_arg;
};

/* Run the finalization functions of ARG.  */
static void
exchange_run_finalizations (struct exchange *exchange, void *arg, int fail)
{
  struct exchange_finalization_node *node = arg;

  node->first (exchange, node->first_arg, fail);
  node->second (exchange, node->second_arg, fail);
  free (node);
}

/*
 * Add a finalization function FINALIZE with argument ARG to the tail
 * of the finalization function list of EXCHANGE.
 */
static void
exchange_add_finalization (struct exchange *exchange,
			   void (*finalize) (struct exchange *, void *, int),
			   void *arg)
{
  struct exchange_finalization_node *node;

  if (!finalize)
    return;

  if (!exchange->finalize)
    {
      exchange->finalize = finalize;
      exchange->finalize_arg = arg;
      return;
    }

  node = malloc (sizeof *node);
  if (!node)
    {
      log_error ("exchange_add_finalization: malloc (%d) failed",
		 sizeof *node);
      free (arg);
      return;
    }
  node->first = exchange->finalize;
  node->first_arg = exchange->finalize_arg;
  node->second = finalize;
  node->second_arg = arg;
  exchange->finalize = exchange_run_finalizations;
  exchange->finalize_arg = node;
}

/* Establish a phase 1 exchange.  */
void
exchange_establish_p1 (struct transport *t, u_int8_t type, u_int32_t doi,
		       char *name, void *args,
		       void (*finalize) (struct exchange *, void *, int),
		       void *arg)
{
  struct exchange *exchange;
  struct message *msg;
  char *tag = 0;
  char *str;

  if (name)
    {
      /* If no exchange type given, fetch from the configuration.  */
      if (type == 0)
	{
	  /* XXX Similar code can be found in exchange_setup_p1.  Share?  */

	  /* Find out our phase 1 mode.  */
	  tag = conf_get_str (name, "Configuration");
	  if (!tag)
	    {
	      /* Use default setting */
	      tag = conf_get_str ("Phase 1", "Default");
	      if (!tag)
		{
		  log_print ("exchange_establish_p1: "
			     "no \"Default\" tag in [Phase 1] section");
		  return;
		}
#if 0
	      log_print ("exchange_establish_p1: "
			 "no configuration found for peer \"%s\"",
			 name);
#endif
	    }

	  /* Figure out the DOI.  XXX Factor out?  */
	  str = conf_get_str (tag, "DOI");
	  if (!str || strcasecmp (str, "IPSEC") == 0)
	    doi = IPSEC_DOI_IPSEC;
	  else if (strcasecmp (str, "ISAKMP") == 0)
	    doi = ISAKMP_DOI_ISAKMP;
	  else
	    {
	      log_print ("exchange_establish_p1: DOI \"%s\" unsupported", str);
	      return;
	    }

	  /* What exchange type do we want?  */
	  str = conf_get_str (tag, "EXCHANGE_TYPE");
	  if (!str)
	    {
	      log_print ("exchange_establish_p1: "
			 "no \"EXCHANGE_TYPE\" tag in [%s] section", tag);
	      return;
	    }
	  type = constant_value (isakmp_exch_cst, str);
	  if (!type)
	    {
	      log_print ("exchange_establish_p1: unknown exchange type %s",
			 str);
	      return;
	    }
	}
    }

  exchange = exchange_create (1, 1, doi, type);
  if (!exchange)
    {
      /* XXX Do something here?  */
      return;
    }

  if (name)
    {
      exchange->name = strdup (name);
      if (!exchange->name)
	{
	  log_error ("exchange_establish_p1: strdup (\"%s\") failed", name);
	  exchange_free (exchange);
	  return;
	}
    }

  exchange->policy = name ? conf_get_str (name, "Configuration") : 0;
  if ((exchange->policy == NULL) && name)
    exchange->policy = conf_get_str ("Phase 1", "Default");

  exchange->finalize = finalize;
  exchange->finalize_arg = arg;
  cookie_gen (t, exchange, exchange->cookies, ISAKMP_HDR_ICOOKIE_LEN);
  exchange_enter (exchange);
  exchange_dump ("exchange_establish_p1", exchange);

  msg = message_alloc (t, 0, ISAKMP_HDR_SZ);
  msg->exchange = exchange;

  /* Do not create SA for an information exchange.  */
  if (exchange->type != ISAKMP_EXCH_INFO)
    {
      /*
       * Don't install a transport into this SA as it will be an INADDR_ANY
       * address in the local end, which is not good at all.  Let the reply
       * packet install the transport instead.
       */
      sa_create (exchange, 0);
      msg->isakmp_sa = TAILQ_FIRST (&exchange->sa_list);
      if (!msg->isakmp_sa)
	{
	  /* XXX Do something more here?  */
	  exchange_free (exchange);
	  return;
	}
      sa_reference (msg->isakmp_sa);
    }

  msg->extra = args;

  exchange_run (msg);
}

/* Establish a phase 2 exchange.  XXX With just one SA for now.  */
void
exchange_establish_p2 (struct sa *isakmp_sa, u_int8_t type, char *name,
		       void *args,
		       void (*finalize) (struct exchange *, void *, int),
		       void *arg)
{
  struct exchange *exchange;
  struct message *msg;
  int i;
  char *tag, *str;
  u_int32_t doi = ISAKMP_DOI_ISAKMP;
  u_int32_t seq = 0;

  if (isakmp_sa)
    doi = isakmp_sa->doi->id;

  if (name)
    {
      /* Find out our phase 2 modes.  */
      tag = conf_get_str (name, "Configuration");
      if (!tag)
	{
	  log_print ("exchange_establish_p2: no configuration for peer \"%s\"",
		     name);
	  return;
	}

      seq = (u_int32_t) conf_get_num (name, "Acquire-ID", 0);

      /* Figure out the DOI.  */
      str = conf_get_str (tag, "DOI");
      if (!str || strcasecmp (str, "IPSEC") == 0)
	doi = IPSEC_DOI_IPSEC;
      else if (strcasecmp (str, "ISAKMP") == 0)
	doi = ISAKMP_DOI_ISAKMP;
      else
	{
	  log_print ("exchange_establish_p2: DOI \"%s\" unsupported", str);
	  return;
	}
      
      /* What exchange type do we want?  */
      if (!type)
	{
	  str = conf_get_str (tag, "EXCHANGE_TYPE");
	  if (!str)
	    {
	      log_print ("exchange_establish_p2: "
			 "no \"EXCHANGE_TYPE\" tag in [%s] section", tag);
	      return;
	    }
	  /* XXX IKE dependent.  */
	  type = constant_value (ike_exch_cst, str);
	  if (!type)
	    {
	      log_print ("exchange_establish_p2: unknown exchange type %s",
			 str);
	      return;
	    }
	}
    }

  exchange = exchange_create (2, 1, doi, type);
  if (!exchange)
    {
      /* XXX Do something here?  */
      return;
    }

  if (name)
    {
      exchange->name = strdup (name);
      if (!exchange->name)
	{
	  log_error ("exchange_establish_p2: strdup (\"%s\") failed", name);
	  exchange_free (exchange);
	  return;
	}
    }
  exchange->policy = name ? conf_get_str (name, "Configuration") : 0;
  exchange->finalize = finalize;
  exchange->finalize_arg = arg;
  exchange->seq = seq;
  memcpy (exchange->cookies, isakmp_sa->cookies, ISAKMP_HDR_COOKIES_LEN);
  getrandom (exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN);
  exchange->flags |= EXCHANGE_FLAG_ENCRYPT;
  exchange_enter (exchange);
  exchange_dump ("exchange_establish_p2", exchange);

  /* 
   * Do not create SA's for informational exchanges. 
   * XXX How to handle new group mode? 
   */
  if (exchange->type != ISAKMP_EXCH_INFO)
    {
      /* XXX Number of SAs should come from the args structure.  */
      for (i = 0; i < 1; i++)
	if (sa_create (exchange, isakmp_sa->transport))
	  {
	    exchange_free (exchange);
	    return;
	  }
    }

  msg = message_alloc (isakmp_sa->transport, 0, ISAKMP_HDR_SZ);
  msg->isakmp_sa = isakmp_sa;
  sa_reference (isakmp_sa);
  
  msg->extra = args;

  /* This needs to be done late or else get_keystate won't work right.  */
  msg->exchange = exchange;

  exchange_run (msg);
}

/* Out of an incoming phase 1 message, setup an exchange.  */
struct exchange *
exchange_setup_p1 (struct message *msg, u_int32_t doi)
{
  struct transport *t = msg->transport;
  struct exchange *exchange;
  struct sockaddr *dst;
  int dst_len;
  char *name = 0, *policy = 0, *str;
  u_int32_t want_doi;
  u_int8_t type;

  /* XXX Similar code can be found in exchange_establish_p1.  Share?  */

  /*
   * Unless this is an informational exchange, look up our policy for this
   * peer.
   */
  type = GET_ISAKMP_HDR_EXCH_TYPE (msg->iov[0].iov_base);
  if (type != ISAKMP_EXCH_INFO)
    {
      /*
       * Find out our inbound phase 1 mode.
       * XXX Assumes IPv4.  It might make sense to search through several
       * policies too.
       */
      t->vtbl->get_dst (t, &dst, &dst_len);
      name = conf_get_str ("Phase 1",
			   inet_ntoa (((struct sockaddr_in *)dst)->sin_addr));
      if (name)
	{
	  /*
	   * If another phase 1 exchange is ongoing don't bother returning the
	   * call. However, we will need to continue responding if our phase 1
	   * exchange is still waiting for step 1 (i.e still half-open).
	   */
	  if (exchange_lookup_active (name, 1))
	    return 0;
	}
      else
	{
	  name = conf_get_str ("Phase 1", "Default");
	  if (!name)
	    {
	      log_print ("exchange_setup_p1: "
			 "no \"Default\" tag in [Phase 1] section");
	      return 0;
	    }
	}

      policy = conf_get_str (name, "Configuration");
      if (!policy)
	{
	  log_print ("exchange_setup_p1: no configuration for peer \"%s\"",
		     name);
	  return 0;
	}

      /* Figure out the DOI.  */
      str = conf_get_str (policy, "DOI");
      if (!str || strcasecmp (str, "IPSEC") == 0)
	want_doi = IPSEC_DOI_IPSEC;
      else if (strcasecmp (str, "ISAKMP") == 0)
	want_doi = ISAKMP_DOI_ISAKMP;
      else
	{
	  log_print ("exchange_setup_p1: DOI \"%s\" unsupported", str);
	  return 0;
	}
      if (want_doi != doi)
	{
	  /* XXX Should I tell what DOI I got?  */
	  log_print ("exchange_setup_p1: expected %s DOI", str);
	  return 0;
	}

      /* What exchange type do we want?  */
      str = conf_get_str (policy, "EXCHANGE_TYPE");
      if (!str)
	{
	  log_print ("exchange_setup_p1: "
		     "no \"EXCHANGE_TYPE\" tag in [%s] section", policy);
	  return 0;
	}
      type = constant_value (isakmp_exch_cst, str);
      if (!type)
	{
	  log_print ("exchange_setup_p1: unknown exchange type %s", str);
	  return 0;
	}
      if (type != GET_ISAKMP_HDR_EXCH_TYPE (msg->iov[0].iov_base))
	{
	  log_print ("exchange_setup_p1: expected exchange type %s got %s",
		     str,
		     constant_lookup (isakmp_exch_cst,
				      GET_ISAKMP_HDR_EXCH_TYPE (msg->iov[0]
								.iov_base)));
	  return 0;
	}
    }

  exchange = exchange_create (1, 0, doi, type);
  if (!exchange)
    return 0;

  exchange->name = name ? strdup (name) : 0;
  if (name && !exchange->name)
    {
      log_error ("exchange_setup_p1: strdup (\"%s\") failed", name);
      exchange_free (exchange);
      return 0;
    }
  exchange->policy = policy;
  cookie_gen (msg->transport, exchange,
	      exchange->cookies + ISAKMP_HDR_ICOOKIE_LEN,
	      ISAKMP_HDR_RCOOKIE_LEN);
  GET_ISAKMP_HDR_ICOOKIE (msg->iov[0].iov_base, exchange->cookies);
  exchange_enter (exchange);
  exchange_dump ("exchange_setup_p1", exchange);
  return exchange;
}

/* Out of an incoming phase 2 message, setup an exchange.  */
struct exchange *
exchange_setup_p2 (struct message *msg, u_int8_t doi)
{
  struct exchange *exchange;
  u_int8_t *buf = msg->iov[0].iov_base;

  exchange = exchange_create (2, 0, doi, GET_ISAKMP_HDR_EXCH_TYPE (buf));
  if (!exchange)
    return 0;
  GET_ISAKMP_HDR_ICOOKIE (buf, exchange->cookies);
  GET_ISAKMP_HDR_RCOOKIE (buf, exchange->cookies + ISAKMP_HDR_ICOOKIE_LEN);
  GET_ISAKMP_HDR_MESSAGE_ID (buf, exchange->message_id);
  exchange_enter (exchange);
  exchange_dump ("exchange_setup_p2", exchange);
  return exchange;
}

/* Dump interesting data about an exchange.  */
static void
exchange_dump_real (char *header, struct exchange *exchange, int class,
		    int level)
{
  char buf[LOG_SIZE];
  /* Don't risk overflowing the final log buffer.  */
  int bufsize_max = LOG_SIZE - strlen (header) - 32; 
  struct sa *sa;

  LOG_DBG ((class, level, 
	    "%s: %p %s %s policy %s phase %d doi %d exchange %d step %d",
	    header, exchange, exchange->name ? exchange->name : "<unnamed>",
	    exchange->policy ? exchange->policy : "<no policy>",
	    exchange->initiator ? "initiator" : "responder", exchange->phase,
	    exchange->doi->id, exchange->type, exchange->step));
  LOG_DBG ((class, level, 
	    "%s: icookie %08x%08x rcookie %08x%08x", header,
	    decode_32 (exchange->cookies), decode_32 (exchange->cookies + 4),
	    decode_32 (exchange->cookies + 8),
	    decode_32 (exchange->cookies + 12)));

  /* Include phase 2 SA list for this exchange */
  if (exchange->phase == 2)
    {
      sprintf (buf, "sa_list ");
      for (sa = TAILQ_FIRST (&exchange->sa_list); 
	   sa && strlen (buf) < bufsize_max; sa = TAILQ_NEXT (sa, next))
	sprintf (buf + strlen (buf), "%p ", sa);
      if (sa)
	strcat (buf, "...");
    }
  else
    buf[0] = '\0';

  LOG_DBG ((class, level, "%s: msgid %08x %s", header, 
	    decode_32 (exchange->message_id), buf));
}

static void
exchange_dump (char *header, struct exchange *exchange)
{
  exchange_dump_real (header, exchange, LOG_EXCHANGE, 10);
}

void
exchange_report (void)
{
  int i;
  struct exchange *exchange;

  for (i = 0; i <= bucket_mask; i++)
    for (exchange = LIST_FIRST (&exchange_tab[i]); exchange;
	 exchange = LIST_NEXT (exchange, link))
      exchange_dump_real ("exchange_report", exchange, LOG_REPORT, 0);
}

/*
 * Release all resources this exchange is using *except* for the "death"
 * event.  When removing an exchange from the expiration handler that event
 * will be dealt with therein instead.
 */
static void
exchange_free_aux (void *v_exch)
{
  struct exchange *exchange = v_exch;
  struct sa *sa, *next_sa;
  struct cert_handler *handler;

  LOG_DBG ((LOG_EXCHANGE, 80, "exchange_free_aux: freeing exchange %p", 
	    exchange));

  if (exchange->last_received)
    message_free (exchange->last_received);
  if (exchange->last_sent)
    message_free (exchange->last_sent);
  if (exchange->in_transit && exchange->in_transit != exchange->last_sent)
    message_free (exchange->in_transit);
  if (exchange->nonce_i)
    free (exchange->nonce_i);
  if (exchange->nonce_r)
    free (exchange->nonce_r);
  if (exchange->id_i)
    free (exchange->id_i);
  if (exchange->id_r)
    free (exchange->id_r);
  if (exchange->keystate)
    free (exchange->keystate);
  if (exchange->doi && exchange->doi->free_exchange_data)
    exchange->doi->free_exchange_data (exchange->data);
  if (exchange->data)
    free (exchange->data);
  if (exchange->name)
    free (exchange->name);
  if (exchange->recv_cert)
    {
      handler = cert_get (exchange->recv_certtype);
      if (handler)
	handler->cert_free (exchange->recv_cert);
      else if (exchange->recv_certtype == ISAKMP_CERTENC_NONE)
	free (exchange->recv_cert);
    }
  if (exchange->recv_key)
    free (exchange->recv_key);

#if defined(POLICY) || defined(KEYNOTE)
  if (exchange->policy_id != -1)
    LK (kn_close, (exchange->policy_id));
#endif

  exchange_free_aca_list (exchange);
  LIST_REMOVE (exchange, link);

  /* Tell potential finalize routine we never got there.  */
  if (exchange->finalize)
    exchange->finalize (exchange, exchange->finalize_arg, 1);

  /* Remove any SAs that has not been disassociated from us.  */
  for (sa = TAILQ_FIRST (&exchange->sa_list); sa; sa = next_sa)
    {
      next_sa = TAILQ_NEXT (sa, next);
      sa_free (sa);
    }

  free (exchange);
}

/* Release all resources this exchange is using.  */
void
exchange_free (struct exchange *exchange)
{
  if (exchange->death)
    timer_remove_event (exchange->death);
  exchange_free_aux (exchange);
}

/*
 * Upgrade the phase 1 exchange and its ISAKMP SA with the rcookie of our
 * peer (found in his recently sent message MSG).
 */
void
exchange_upgrade_p1 (struct message *msg)
{
  struct exchange *exchange = msg->exchange;

  LIST_REMOVE (exchange, link);
  GET_ISAKMP_HDR_RCOOKIE (msg->iov[0].iov_base,
			  exchange->cookies + ISAKMP_HDR_ICOOKIE_LEN);
  exchange_enter (exchange);
  sa_isakmp_upgrade (msg);
}

static int
exchange_check_old_sa (struct sa *sa, void *v_arg)
{
  struct sa *new_sa = v_arg;
  char res1[1024];

  if (sa == new_sa || !sa->name || !(sa->flags & SA_FLAG_READY) || 
      (sa->flags & SA_FLAG_REPLACED))
    return 0;

  if (sa->phase != new_sa->phase || new_sa->name == NULL ||
      strcasecmp (sa->name, new_sa->name))
    return 0;

  if (sa->initiator)
    strlcpy (res1, ipsec_decode_ids ("%s %s", sa->id_i, sa->id_i_len, sa->id_r,
				     sa->id_r_len, 0), sizeof res1);
  else
    strlcpy (res1, ipsec_decode_ids ("%s %s", sa->id_r, sa->id_r_len, sa->id_i,
				     sa->id_i_len, 0), sizeof res1);

  LOG_DBG ((LOG_EXCHANGE, 30,
	    "checking whether new SA replaces existing SA with IDs %s",
	    res1));

  if (new_sa->initiator)
    return strcasecmp (res1, ipsec_decode_ids ("%s %s", new_sa->id_i,
					       new_sa->id_i_len,
					       new_sa->id_r,
					       new_sa->id_r_len, 0)) == 0;
  else
    return strcasecmp (res1, ipsec_decode_ids ("%s %s", new_sa->id_r,
					       new_sa->id_r_len,
					       new_sa->id_i,
					       new_sa->id_i_len, 0)) == 0;
}

void
exchange_finalize (struct message *msg)
{
  struct exchange *exchange = msg->exchange;
  struct sa *sa, *old_sa;
  struct proto *proto;
  struct conf_list *attrs;
  struct conf_list_node *attr;
  int i;

  exchange_dump ("exchange_finalize", exchange);

  /*
   * Walk over all the SAs and noting them as ready.  If we set the
   * COMMIT bit, tell the peer each SA is connected.
   *
   * XXX The decision should really be based on if a SA was installed
   * successfully.
   */
  for (sa = TAILQ_FIRST (&exchange->sa_list); sa; sa = TAILQ_NEXT (sa, next))
    {
      /* Move over the name to the SA.  */
      sa->name = exchange->name ? strdup (exchange->name) : 0;

      if (exchange->flags & EXCHANGE_FLAG_I_COMMITTED)
	{
	  for (proto = TAILQ_FIRST (&sa->protos); proto;
	       proto = TAILQ_NEXT (proto, link))
	    for (i = 0; i < 2; i++)
	      message_send_notification (exchange->last_received,
					 msg->isakmp_sa,
					 ISAKMP_NOTIFY_STATUS_CONNECTED, proto,
					 i);
	}

      /* Locate any old SAs and mark them replaced (SA_FLAG_REPLACED).  */
      while ((old_sa = sa_find (exchange_check_old_sa, sa)) != 0)
	sa_mark_replaced (old_sa);

      /* Setup the SA flags.  */
      sa->flags |= SA_FLAG_READY;
      if (exchange->name)
	{
	  attrs = conf_get_list (exchange->name, "Flags");
	  if (attrs)
	    {
	      for (attr = TAILQ_FIRST (&attrs->fields); attr;
		   attr = TAILQ_NEXT (attr, link))
		sa->flags |= sa_flag (attr->field);
	      conf_free_list (attrs);
	    }
	  /* 'Connections' should stay alive.  */
	  if (connection_exist (exchange->name))
	    {
	      sa->flags |= SA_FLAG_STAYALIVE;

	      /* ISAKMP SA of this connection should also stay alive.  */
	      if (exchange->phase == 2 && msg->isakmp_sa)
		msg->isakmp_sa->flags |= SA_FLAG_STAYALIVE;
	    }
	}

      sa->exch_type = exchange->type;
    }

  /*
   * If this was an phase 1 SA negotiation, save the keystate in the ISAKMP SA
   * structure for future initialization of phase 2 exchanges' keystates.
   * Also save the Phase 1 ID and authentication information.
   */
  if (exchange->phase == 1 && msg->isakmp_sa)
    {
      msg->isakmp_sa->keystate = exchange->keystate;
      exchange->keystate = 0;

      msg->isakmp_sa->recv_certtype = exchange->recv_certtype;
      msg->isakmp_sa->recv_certlen = exchange->recv_certlen;
      msg->isakmp_sa->recv_key = exchange->recv_key;
      exchange->recv_key = NULL; /* Reset */
      msg->isakmp_sa->policy_id = exchange->policy_id;
      exchange->policy_id = -1; /* Reset */
      msg->isakmp_sa->id_i_len = exchange->id_i_len;
      msg->isakmp_sa->id_r_len = exchange->id_r_len;
      msg->isakmp_sa->initiator = exchange->initiator;

      switch (exchange->recv_certtype)
        {
        case ISAKMP_CERTENC_NONE:
	case ISAKMP_CERTENC_KEYNOTE: /* No need for special handling */
	    msg->isakmp_sa->recv_cert = malloc (exchange->recv_certlen);
	    if (!msg->isakmp_sa->recv_cert)
	      {
		log_error ("exchange_finalize: malloc (%d) failed",
			   exchange->recv_certlen);
		/* XXX How to cleanup?  */
		return;
	      }
	    memcpy (msg->isakmp_sa->recv_cert, exchange->recv_cert,
		    msg->isakmp_sa->recv_certlen);
	    break;

	case ISAKMP_CERTENC_X509_SIG:
#ifdef USE_X509
	    msg->isakmp_sa->recv_cert = LC (X509_dup,
					    ((X509 *) exchange->recv_cert));
	    if (!msg->isakmp_sa->recv_cert)
	      {
		log_print ("exchange_finalize: "
			   "failed copying X509 certificate to isakmp_sa");
		/* XXX How to cleanup?  */
		return;
	      }
	    break;
#endif

	    /* XXX Eventually handle these */
	case ISAKMP_CERTENC_PKCS:
	case ISAKMP_CERTENC_PGP:	
	case ISAKMP_CERTENC_DNS:
	case ISAKMP_CERTENC_X509_KE:
	case ISAKMP_CERTENC_KERBEROS:
	case ISAKMP_CERTENC_CRL:
	case ISAKMP_CERTENC_ARL:
	case ISAKMP_CERTENC_SPKI:
	case ISAKMP_CERTENC_X509_ATTR:
	}

      LOG_DBG ((LOG_EXCHANGE, 10,
		"exchange_finalize: phase 1 done: %s, %s",
		exchange->doi == NULL ? "<no doi>" :
		exchange->doi->decode_ids ("initiator id %s, responder id %s",
					   exchange->id_i, exchange->id_i_len,
					   exchange->id_r, exchange->id_r_len,
					   0),
		msg->isakmp_sa == NULL || msg->isakmp_sa->transport == NULL
		? "<no transport>"
		: msg->isakmp_sa->transport->vtbl->decode_ids (msg->isakmp_sa->transport)));
    }

  exchange->doi->finalize_exchange (msg);
  if (exchange->finalize)
    exchange->finalize (exchange, exchange->finalize_arg, 0);
  exchange->finalize = 0;

  /* copy the ID from phase 1 to exchange or phase 2 SA */
  if (msg->isakmp_sa) 
    {
      if (exchange->id_i && exchange->id_r) 
	{
	  ipsec_clone_id (&msg->isakmp_sa->id_i, &msg->isakmp_sa->id_i_len,
			  exchange->id_i, exchange->id_i_len);
	  ipsec_clone_id (&msg->isakmp_sa->id_r, &msg->isakmp_sa->id_r_len,
			  exchange->id_r, exchange->id_r_len);
	}
      else if (msg->isakmp_sa->id_i && msg->isakmp_sa->id_r)
	{
	  ipsec_clone_id (&exchange->id_i, &exchange->id_i_len,
			  msg->isakmp_sa->id_i, msg->isakmp_sa->id_i_len);
	  ipsec_clone_id (&exchange->id_r, &exchange->id_r_len,
			  msg->isakmp_sa->id_r, msg->isakmp_sa->id_r_len);
	}
    }

  /*
   * There is no reason to keep the SAs connected to us anymore, in fact
   * it can hurt us if we have short lifetimes on the SAs and we try
   * to call exchange_report, where the SA list will be walked and
   * references to freed SAs can occur.
   */
  while (TAILQ_FIRST (&exchange->sa_list))
    {
      struct sa *sa = TAILQ_FIRST (&exchange->sa_list);

      if (exchange->id_i && exchange->id_r)
	{
          ipsec_clone_id (&sa->id_i, &sa->id_i_len, exchange->id_i,
		          exchange->id_i_len);
          ipsec_clone_id (&sa->id_r, &sa->id_r_len, exchange->id_r,
		          exchange->id_r_len);
	}

      TAILQ_REMOVE (&exchange->sa_list, sa, next);
      sa_release (sa);
    }

  /* If we have nothing to retransmit we can safely remove ourselves.  */
  if (!exchange->last_sent)
    exchange_free (exchange);
}

/* Stash a nonce into the exchange data.  */
static int
exchange_nonce (struct exchange *exchange, int peer, size_t nonce_sz,
		u_int8_t *buf)
{
  int initiator = exchange->initiator ^ peer;
  u_int8_t **nonce;
  size_t *nonce_len;
  char header[32];

  nonce = initiator ? &exchange->nonce_i : &exchange->nonce_r;
  nonce_len = initiator ? &exchange->nonce_i_len : &exchange->nonce_r_len;
  *nonce_len = nonce_sz;
  *nonce = malloc (nonce_sz);
  if (!*nonce)
    {
      log_error ("exchange_nonce: malloc (%d) failed", nonce_sz);
      return -1;
    }
  memcpy (*nonce, buf, nonce_sz);
  snprintf (header, 32, "exchange_nonce: NONCE_%c", initiator ? 'i' : 'r');
  LOG_DBG_BUF ((LOG_EXCHANGE, 80, header, *nonce, nonce_sz));
  return 0;
}

/* Generate our NONCE.  */
int
exchange_gen_nonce (struct message *msg, size_t nonce_sz)
{
  struct exchange *exchange = msg->exchange;
  u_int8_t *buf;

  buf = malloc (ISAKMP_NONCE_SZ + nonce_sz);
  if (!buf)
    {
      log_error ("exchange_gen_nonce: malloc (%d) failed",
		 ISAKMP_NONCE_SZ + nonce_sz);
      return -1;
    }
  getrandom (buf + ISAKMP_NONCE_DATA_OFF, nonce_sz);
  if (message_add_payload (msg, ISAKMP_PAYLOAD_NONCE, buf,
			   ISAKMP_NONCE_SZ + nonce_sz, 1))
    {
      free (buf);
      return -1;
    }
  return exchange_nonce (exchange, 0, nonce_sz, buf + ISAKMP_NONCE_DATA_OFF);
}

/* Save the peer's NONCE.  */
int
exchange_save_nonce (struct message *msg)
{
  struct payload *noncep;
  struct exchange *exchange = msg->exchange;

  noncep = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_NONCE]);
  noncep->flags |= PL_MARK;
  return exchange_nonce (exchange, 1,
			 GET_ISAKMP_GEN_LENGTH (noncep->p)
			 - ISAKMP_NONCE_DATA_OFF,
			 noncep->p + ISAKMP_NONCE_DATA_OFF);
}

/* Save the peer's CERT REQuests.  */
int
exchange_save_certreq (struct message *msg)
{
  struct payload *cp = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_CERT_REQ]);
  struct exchange *exchange = msg->exchange;
  struct certreq_aca *tmp;

  for ( ; cp; cp = TAILQ_NEXT (cp, link))
    {
      cp->flags |= PL_MARK;
      tmp = certreq_decode (GET_ISAKMP_CERTREQ_TYPE (cp->p),
			    cp->p + ISAKMP_CERTREQ_AUTHORITY_OFF,
			    GET_ISAKMP_GEN_LENGTH (cp->p) - 
			    ISAKMP_CERTREQ_AUTHORITY_OFF);
      if (!tmp)
	continue;
      TAILQ_INSERT_TAIL (&exchange->aca_list, tmp, link);
    }

  return 0;
}

/* Free the list of pending CERTREQ */

void
exchange_free_aca_list (struct exchange *exchange)
{
  struct certreq_aca *aca;

  for (aca = TAILQ_FIRST (&exchange->aca_list); aca;
       aca = TAILQ_FIRST (&exchange->aca_list))
    {
      if (aca->data)
	{
	  if (aca->handler)
	    aca->handler->free_aca (aca->data);
	  free (aca->data);
	}
      TAILQ_REMOVE (&exchange->aca_list, aca, link);
      free (aca);
    }
}

/* Obtain certificates from acceptable certification authority.  */
int
exchange_add_certs (struct message *msg)
{
  struct exchange *exchange = msg->exchange;
  struct certreq_aca *aca;
  u_int8_t *cert;
  u_int32_t certlen;
  u_int8_t *id;
  size_t id_len;

  id = exchange->initiator ? exchange->id_r : exchange->id_i;
  id_len = exchange->initiator ? exchange->id_r_len : exchange->id_i_len;

  for (aca = TAILQ_FIRST (&exchange->aca_list); aca; 
       aca = TAILQ_NEXT (aca, link))
    {
      /* XXX? If we can not satisfy a CERTREQ we drop the message */
      if (!aca->handler->cert_obtain (id, id_len, aca->data, &cert, &certlen))
	{
	  log_print ("exchange_add_certs: could not obtain cert for a type %d "
		     "cert request", aca->id);
	  return -1;
	}
      cert = realloc (cert, ISAKMP_CERT_SZ + certlen);
      if (!cert)
	{
	  log_error ("exchange_add_certs: realloc (%p, %d) failed", cert,
		     ISAKMP_CERT_SZ + certlen);
	  return -1;
	}
      memmove (cert + ISAKMP_CERT_DATA_OFF, cert, certlen);
      SET_ISAKMP_CERT_ENCODING (cert, aca->id);
      if (message_add_payload (msg, ISAKMP_PAYLOAD_CERT, cert,
			       ISAKMP_CERT_SZ + certlen, 1))
	{
	  free (cert);
	  return -1;
	}
    }

  /* We dont need the CERT REQs any more, they are anwsered */
  exchange_free_aca_list (exchange);

  return 0;
}

static void
exchange_establish_finalize (struct exchange *exchange, void *arg, int fail)
{
  char *name = arg;

  LOG_DBG ((LOG_EXCHANGE, 20,
	    "exchange_establish_finalize: "
	    "finalizing exchange %p with arg %p (%s) & fail = %d",
	    exchange, arg, name ? name : "<unnamed>", fail));

  if (!fail)
    exchange_establish (name, 0, 0);
  free (name);
}

/*
 * Establish an exchange named NAME, and record the FINALIZE function
 * taking ARG as an argument to be run after the exchange is ready.
 */
void
exchange_establish (char *name,
		    void (*finalize) (struct exchange *, void *, int),
		    void *arg)
{
  int phase;
  char *trpt;
  struct transport *transport;
  char *peer;
  struct sa *isakmp_sa;
  struct exchange *exchange;
  phase = conf_get_num (name, "Phase", 0);

  /*
   * First of all, never try to establish anything if another exchange of the
   * same kind is running.
   */
  exchange = exchange_lookup_by_name (name, phase);
  if (exchange)
    {
      LOG_DBG ((LOG_EXCHANGE, 40,
		"exchange_establish: %s exchange already exists as %p", name,
		exchange));
      exchange_add_finalization (exchange, finalize, arg);
      return;
    }

  switch (phase)
    {
    case 1:
      trpt = conf_get_str (name, "Transport");
      if (!trpt)
	{
	  /* Phase 1 transport defaults to "udp".  */
	  trpt = ISAKMP_DEFAULT_TRANSPORT;
	}

      transport = transport_create (trpt, name);
      if (!transport)
	{
	  log_print ("exchange_establish: "
		     "transport \"%s\" for peer \"%s\" could not be created",
		     trpt, name);
	  return;
	}

      exchange_establish_p1 (transport, 0, 0, name, 0, finalize, arg);
      break;

    case 2:
      peer = conf_get_str (name, "ISAKMP-peer");
      if (!peer)
	{
	  log_print ("exchange_establish: No ISAKMP-peer given for \"%s\"",
		     name);
	  return;
	}

      isakmp_sa = sa_lookup_by_name (peer, 1);
      if (!isakmp_sa)
	{
	  name = strdup (name);
	  if (!name)
	    {
	      log_error ("exchange_establish: strdup(\"%s\") failed", name);
	      return;
	    }

	  if (conf_get_num (peer, "Phase", 0) != 1)
	    {
	      log_print ("exchange_establish: "
			 "[%s]:ISAKMP-peer's (%s) phase is not 1", name, peer);
	      return;
	    }

	  exchange_establish (peer, exchange_establish_finalize, name);
	}
      else
	exchange_establish_p2 (isakmp_sa, 0, name, 0, finalize, arg);
      break;

    default:
      log_print ("exchange_establish: "
		 "peer \"%s\" does not have a correct phase (%d)",
		 name, phase);
      break;
    }
}