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

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

Revision 1.28, Mon Apr 9 12:34:38 2001 UTC (23 years, 1 month ago) by ho
Branch: MAIN
CVS Tags: OPENBSD_2_9_BASE, OPENBSD_2_9
Changes since 1.27: +6 -6 lines

Style police a'la niklas@.

/*	$OpenBSD: policy.c,v 1.28 2001/04/09 12:34:38 ho Exp $	*/
/*	$EOM: policy.c,v 1.49 2000/10/24 13:33:39 niklas Exp $ */

/*
 * Copyright (c) 1999, 2000, 2001 Angelos D. Keromytis.  All rights reserved.
 * Copyright (c) 1999, 2000, 2001 Niklas Hallqvist.  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/param.h>
#include <sys/mman.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <regex.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <keynote.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <openssl/ssl.h>

#include "sysdep.h"

#include "app.h"
#include "conf.h"
#include "connection.h"
#include "cookie.h"
#include "doi.h"
#include "dyn.h"
#include "exchange.h"
#include "init.h"
#include "ipsec.h"
#include "isakmp_doi.h"
#include "math_group.h"
#include "sa.h"
#include "timer.h"
#include "transport.h"
#include "udp.h"
#include "log.h"
#include "message.h"
#include "ui.h"
#include "util.h"
#include "policy.h"
#include "x509.h"

#if defined (HAVE_DLOPEN) && !defined (USE_KEYNOTE) && 0

void *libkeynote = 0;

/*
 * These prototypes matches OpenBSD keynote.h 1.6.  If you use
 * a different version than that, you are on your own.
 */
int *lk_keynote_errno;
int (*lk_kn_add_action) (int, char *, char *, int);
int (*lk_kn_add_assertion) (int, char *, int, int);
int (*lk_kn_add_authorizer) (int, char *);
int (*lk_kn_close) (int);
int (*lk_kn_do_query) (int, char **, int);
char *(*lk_kn_encode_key) (struct keynote_deckey *, int, int, int);
int (*lk_kn_init) (void);
char **(*lk_kn_read_asserts) (char *, int, int *);
int (*lk_kn_remove_authorizer) (int, char *);
int (*lk_kn_get_authorizer) (int, int, int *);
void (*lk_kn_free_key) (struct keynote_deckey *);
struct keynote_keylist *(*lk_kn_get_licensees) (int, int);
#define SYMENTRY(x) { SYM, SYM (x), (void **)&lk_ ## x }

static struct dynload_script libkeynote_script[] = {
  { LOAD, "libc.so", &libkeynote },
  { LOAD, "libcrypto.so", &libkeynote },
  { LOAD, "libm.so", &libkeynote },
  { LOAD, "libkeynote.so", &libkeynote },
  SYMENTRY (keynote_errno),
  SYMENTRY (kn_add_action),
  SYMENTRY (kn_add_assertion),
  SYMENTRY (kn_add_authorizer),
  SYMENTRY (kn_close),
  SYMENTRY (kn_do_query),
  SYMENTRY (kn_encode_key),
  SYMENTRY (kn_init),
  SYMENTRY (kn_read_asserts),
  SYMENTRY (kn_remove_authorizer),
  SYMENTRY (kn_get_licensees),
  SYMENTRY (kn_get_authorizer),
  { EOS }
};
#endif

int keynote_sessid = -1;
char **keynote_policy_asserts = NULL;
int keynote_policy_asserts_num = 0;
char **x509_policy_asserts = NULL;
int x509_policy_asserts_num = 0;
int x509_policy_asserts_num_alloc = 0;
struct exchange *policy_exchange = 0;
struct sa *policy_sa = 0;
struct sa *policy_isakmp_sa = 0;

static const char hextab[] = {
  '0', '1', '2', '3', '4', '5', '6', '7',
  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};

/*
 * Adaptation of Vixie's inet_ntop4 ()
 */
static const char *
my_inet_ntop4 (const in_addr_t *src, char *dst, size_t size, int normalize)
{
  static const char fmt[] = "%03u.%03u.%03u.%03u";
  char tmp[sizeof "255.255.255.255"];
  in_addr_t src2;

  if (normalize)
    src2 = ntohl (*src);
  else
    src2 = *src;

  if (sprintf (tmp, fmt, ((u_int8_t *) &src2)[0], ((u_int8_t *) &src2)[1],
	       ((u_int8_t *) &src2)[2], ((u_int8_t *) &src2)[3]) > size)
    {
      errno = ENOSPC;
      return 0;
    }
  strcpy (dst, tmp);
  return dst;
}

char *
policy_callback (char *name)
{
  struct proto *proto;

  u_int8_t *attr, *value, *id, *idlocal, *idremote;
  size_t id_sz, idlocalsz, idremotesz;
  struct sockaddr_in *sin;
  struct ipsec_exch *ie;
  struct ipsec_sa *is;
  int fmt, i, lifetype = 0;
  in_addr_t net, subnet;
  u_int16_t len, type;
  time_t tt;
  static char mytimeofday[15];

  /* We use all these as a cache.  */
  static char *esp_present, *ah_present, *comp_present;
  static char *ah_hash_alg, *ah_auth_alg, *esp_auth_alg, *esp_enc_alg;
  static char *comp_alg, ah_life_kbytes[32], ah_life_seconds[32];
  static char esp_life_kbytes[32], esp_life_seconds[32], comp_life_kbytes[32];
  static char comp_life_seconds[32], *ah_encapsulation, *esp_encapsulation;
  static char *comp_encapsulation, ah_key_length[32], esp_key_length[32];
  static char ah_key_rounds[32], esp_key_rounds[32], comp_dict_size[32];
  static char comp_private_alg[32], *remote_filter_type, *local_filter_type;
  static char remote_filter_addr_upper[64], remote_filter_addr_lower[64];
  static char local_filter_addr_upper[64], local_filter_addr_lower[64];
  static char ah_group_desc[32], esp_group_desc[32], comp_group_desc[32];
  static char remote_ike_address[64], local_ike_address[64];
  static char *remote_id_type, remote_id_addr_upper[64], *phase_1;
  static char remote_id_addr_lower[64], *remote_id_proto, remote_id_port[32];
  static char remote_filter_port[32], local_filter_port[32];
  static char *remote_filter_proto, *local_filter_proto, *pfs, *initiator;
  static char remote_filter_proto_num[3], local_filter_proto_num[3];
  static char remote_id_proto_num[3];
  static char phase1_group[32];

  /* Allocated.  */
  static char *remote_filter = 0, *local_filter = 0, *remote_id = 0;

  static int dirty = 1;

  /* We only need to set dirty at initialization time really.  */
  if (strcmp (name, KEYNOTE_CALLBACK_CLEANUP) == 0
      || strcmp (name, KEYNOTE_CALLBACK_INITIALIZE) == 0)
    {
      esp_present = ah_present = comp_present = pfs = "no";
      ah_hash_alg = ah_auth_alg = phase_1 = "";
      esp_auth_alg = esp_enc_alg = comp_alg = ah_encapsulation = "";
      esp_encapsulation = comp_encapsulation = remote_filter_type = "";
      local_filter_type = remote_id_type = initiator = "";
      remote_filter_proto = local_filter_proto = remote_id_proto = "";

      if (remote_filter != 0)
        {
	  free (remote_filter);
	  remote_filter = 0;
	}

      if (local_filter != 0)
        {
	  free (local_filter);
	  local_filter = 0;
	}

      if (remote_id != 0)
        {
	  free (remote_id);
	  remote_id = 0;
	}

      memset (remote_ike_address, 0, sizeof remote_ike_address);
      memset (local_ike_address, 0, sizeof local_ike_address);
      memset (ah_life_kbytes, 0, sizeof ah_life_kbytes);
      memset (ah_life_seconds, 0, sizeof ah_life_seconds);
      memset (esp_life_kbytes, 0, sizeof esp_life_kbytes);
      memset (esp_life_seconds, 0, sizeof esp_life_seconds);
      memset (comp_life_kbytes, 0, sizeof comp_life_kbytes);
      memset (comp_life_seconds, 0, sizeof comp_life_seconds);
      memset (ah_key_length, 0, sizeof ah_key_length);
      memset (ah_key_rounds, 0, sizeof ah_key_rounds);
      memset (esp_key_length, 0, sizeof esp_key_length);
      memset (esp_key_rounds, 0, sizeof esp_key_rounds);
      memset (comp_dict_size, 0, sizeof comp_dict_size);
      memset (comp_private_alg, 0, sizeof comp_private_alg);
      memset (remote_filter_addr_upper, 0, sizeof remote_filter_addr_upper);
      memset (remote_filter_addr_lower, 0, sizeof remote_filter_addr_lower);
      memset (local_filter_addr_upper, 0, sizeof local_filter_addr_upper);
      memset (local_filter_addr_lower, 0, sizeof local_filter_addr_lower);
      memset (remote_id_addr_upper, 0, sizeof remote_id_addr_upper);
      memset (remote_id_addr_lower, 0, sizeof remote_id_addr_lower);
      memset (ah_group_desc, 0, sizeof ah_group_desc);
      memset (esp_group_desc, 0, sizeof esp_group_desc);
      memset (remote_id_port, 0, sizeof remote_id_port);
      memset (remote_filter_port, 0, sizeof remote_filter_port);
      memset (local_filter_port, 0, sizeof local_filter_port);
      memset (phase1_group, 0, sizeof phase1_group);

      dirty = 1;
      return "";
    }

  /*
   * If dirty is set, this is the first request for an attribute, so
   * populate our value cache.
   */
  if (dirty)
    {
      ie = policy_exchange->data;

      if (ie->pfs)
	pfs = "yes";

      is = policy_isakmp_sa->data;
      sprintf (phase1_group, "%u", is->group_desc);

      for (proto = TAILQ_FIRST (&policy_sa->protos); proto;
	   proto = TAILQ_NEXT (proto, link))
	{
	  switch (proto->proto)
	    {
	    case IPSEC_PROTO_IPSEC_AH:
	      ah_present = "yes";
	      switch (proto->id)
		{
		case IPSEC_AH_MD5:
		  ah_hash_alg = "md5";
		  break;

		case IPSEC_AH_SHA:
		  ah_hash_alg = "sha";
		  break;

		case IPSEC_AH_RIPEMD:
		  ah_hash_alg = "ripemd";
		  break;

		case IPSEC_AH_DES:
		  ah_hash_alg = "des";
		  break;
		}

	      break;

	    case IPSEC_PROTO_IPSEC_ESP:
	      esp_present = "yes";
	      switch (proto->id)
		{
		case IPSEC_ESP_DES_IV64:
		  esp_enc_alg = "des-iv64";
		  break;

		case IPSEC_ESP_DES:
		  esp_enc_alg = "des";
		  break;

		case IPSEC_ESP_3DES:
		  esp_enc_alg = "3des";
		  break;

		case IPSEC_ESP_AES:
		  esp_enc_alg = "aes";
		  break;

		case IPSEC_ESP_RC5:
		  esp_enc_alg = "rc5";
		  break;

		case IPSEC_ESP_IDEA:
		  esp_enc_alg = "idea";
		  break;

		case IPSEC_ESP_CAST:
		  esp_enc_alg = "cast";
		  break;

		case IPSEC_ESP_BLOWFISH:
		  esp_enc_alg = "blowfish";
		  break;

		case IPSEC_ESP_3IDEA:
		  esp_enc_alg = "3idea";
		  break;

		case IPSEC_ESP_DES_IV32:
		  esp_enc_alg = "des-iv32";
		  break;

		case IPSEC_ESP_RC4:
		  esp_enc_alg = "rc4";
		  break;

		case IPSEC_ESP_NULL:
		  esp_enc_alg = "null";
		  break;
		}

	      break;

	    case IPSEC_PROTO_IPCOMP:
	      comp_present = "yes";
	      switch (proto->id)
		{
		case IPSEC_IPCOMP_OUI:
		  comp_alg = "oui";
		  break;

		case IPSEC_IPCOMP_DEFLATE:
		  comp_alg = "deflate";
		  break;

		case IPSEC_IPCOMP_LZS:
		  comp_alg = "lzs";
		  break;

		case IPSEC_IPCOMP_V42BIS:
		  comp_alg = "v42bis";
		  break;
		}

	      break;
	    }

	  for (attr = proto->chosen->p + ISAKMP_TRANSFORM_SA_ATTRS_OFF;
	       attr
		 < proto->chosen->p + GET_ISAKMP_GEN_LENGTH (proto->chosen->p);
	       attr = value + len)
	    {
	      if (attr + ISAKMP_ATTR_VALUE_OFF
		  > (proto->chosen->p
		     + GET_ISAKMP_GEN_LENGTH (proto->chosen->p)))
		return "";

	      type = GET_ISAKMP_ATTR_TYPE (attr);
	      fmt = ISAKMP_ATTR_FORMAT (type);
	      type = ISAKMP_ATTR_TYPE (type);
	      value = attr + (fmt ? ISAKMP_ATTR_LENGTH_VALUE_OFF :
			      ISAKMP_ATTR_VALUE_OFF);
	      len = (fmt ? ISAKMP_ATTR_LENGTH_VALUE_LEN :
		     GET_ISAKMP_ATTR_LENGTH_VALUE (attr));

	      if (value + len > proto->chosen->p +
		  GET_ISAKMP_GEN_LENGTH (proto->chosen->p))
		return "";

	      switch (type)
		{
		case IPSEC_ATTR_SA_LIFE_TYPE:
		  lifetype = decode_16 (value);
		  break;

		case IPSEC_ATTR_SA_LIFE_DURATION:
		  switch (proto->proto)
		    {
		    case IPSEC_PROTO_IPSEC_AH:
		      if (lifetype == IPSEC_DURATION_SECONDS)
			{
			  if (len == 2)
			    sprintf (ah_life_seconds, "%u",
				     decode_16 (value));
			  else
			    sprintf (ah_life_seconds, "%u",
				     decode_32 (value));
			}
		      else
			{
			  if (len == 2)
			    sprintf (ah_life_kbytes, "%u",
				     decode_16 (value));
			  else
			    sprintf (ah_life_kbytes, "%u",
				     decode_32 (value));
			}

		      break;

		    case IPSEC_PROTO_IPSEC_ESP:
		      if (lifetype == IPSEC_DURATION_SECONDS)
			{
			  if (len == 2)
			    sprintf (esp_life_seconds, "%u",
				     decode_16 (value));
			  else
			    sprintf (esp_life_seconds, "%u",
				     decode_32 (value));
			}
		      else
			{
			  if (len == 2)
			    sprintf (esp_life_kbytes, "%u",
				     decode_16 (value));
			  else
			    sprintf (esp_life_kbytes, "%u",
				     decode_32 (value));
			}

		      break;

		    case IPSEC_PROTO_IPCOMP:
		      if (lifetype == IPSEC_DURATION_SECONDS)
			{
			  if (len == 2)
			    sprintf (comp_life_seconds, "%u",
				     decode_16 (value));
			  else
			    sprintf (comp_life_seconds, "%u",
				     decode_32 (value));
			}
		      else
			{
			  if (len == 2)
			    sprintf (comp_life_kbytes, "%u",
				     decode_16 (value));
			  else
			    sprintf (comp_life_kbytes, "%u",
				     decode_32 (value));
			}

		      break;
		    }
		  break;

		case IPSEC_ATTR_GROUP_DESCRIPTION:
		  switch (proto->proto)
		    {
		    case IPSEC_PROTO_IPSEC_AH:
		      sprintf (ah_group_desc, "%u", decode_16 (value));
		      break;

		    case IPSEC_PROTO_IPSEC_ESP:
		      sprintf (esp_group_desc, "%u",
			       decode_16 (value));
		      break;

		    case IPSEC_PROTO_IPCOMP:
		      sprintf (comp_group_desc, "%u",
			       decode_16 (value));
		      break;
		    }
		  break;

		case IPSEC_ATTR_ENCAPSULATION_MODE:
		  if (decode_16 (value) == IPSEC_ENCAP_TUNNEL)
		    switch (proto->proto)
		      {
		      case IPSEC_PROTO_IPSEC_AH:
			ah_encapsulation = "tunnel";
			break;

		      case IPSEC_PROTO_IPSEC_ESP:
			esp_encapsulation = "tunnel";
			break;

		      case IPSEC_PROTO_IPCOMP:
			comp_encapsulation = "tunnel";
			break;
		      }
		  else
		    switch (proto->proto)
		      {
		      case IPSEC_PROTO_IPSEC_AH:
			ah_encapsulation = "transport";
			break;

		      case IPSEC_PROTO_IPSEC_ESP:
			esp_encapsulation = "transport";
			break;

		      case IPSEC_PROTO_IPCOMP:
			comp_encapsulation = "transport";
			break;
		      }
		  break;

		case IPSEC_ATTR_AUTHENTICATION_ALGORITHM:
		  switch (proto->proto)
		    {
		    case IPSEC_PROTO_IPSEC_AH:
		      switch (decode_16 (value))
			{
			case IPSEC_AUTH_HMAC_MD5:
			  ah_auth_alg = "hmac-md5";
			  break;

			case IPSEC_AUTH_HMAC_SHA:
			  ah_auth_alg = "hmac-sha";
			  break;

			case IPSEC_AUTH_HMAC_RIPEMD:
			  ah_auth_alg = "hmac-ripemd";
			  break;

			case IPSEC_AUTH_DES_MAC:
			  ah_auth_alg = "des-mac";
			  break;

			case IPSEC_AUTH_KPDK:
			  ah_auth_alg = "kpdk";
			  break;
			}
		      break;

		    case IPSEC_PROTO_IPSEC_ESP:
		      switch (decode_16 (value))
			{
			case IPSEC_AUTH_HMAC_MD5:
			  esp_auth_alg = "hmac-md5";
			  break;

			case IPSEC_AUTH_HMAC_SHA:
			  esp_auth_alg = "hmac-sha";
			  break;

			case IPSEC_AUTH_HMAC_RIPEMD:
			  esp_auth_alg = "hmac-ripemd";
			  break;

			case IPSEC_AUTH_DES_MAC:
			  esp_auth_alg = "des-mac";
			  break;

			case IPSEC_AUTH_KPDK:
			  esp_auth_alg = "kpdk";
			  break;
			}
		      break;
		    }
		  break;

		case IPSEC_ATTR_KEY_LENGTH:
		  switch (proto->proto)
		    {
		    case IPSEC_PROTO_IPSEC_AH:
		      sprintf (ah_key_length, "%u", decode_16 (value));
		      break;

		    case IPSEC_PROTO_IPSEC_ESP:
		      sprintf (esp_key_length, "%u",
			       decode_16 (value));
		      break;
		    }
		  break;

		case IPSEC_ATTR_KEY_ROUNDS:
		  switch (proto->proto)
		    {
		    case IPSEC_PROTO_IPSEC_AH:
		      sprintf (ah_key_rounds, "%u", decode_16 (value));
		      break;

		    case IPSEC_PROTO_IPSEC_ESP:
		      sprintf (esp_key_rounds, "%u",
			       decode_16 (value));
		      break;
		    }
		  break;

		case IPSEC_ATTR_COMPRESS_DICTIONARY_SIZE:
		  sprintf (comp_dict_size, "%u", decode_16 (value));
		  break;

		case IPSEC_ATTR_COMPRESS_PRIVATE_ALGORITHM:
		  sprintf (comp_private_alg, "%u", decode_16 (value));
		  break;
		}
	    }
	}

      /* XXX IPv4-specific.  */
      policy_sa->transport->vtbl->get_src (policy_sa->transport,
					   (struct sockaddr **)&sin, &fmt);
      my_inet_ntop4 (&(sin->sin_addr.s_addr), local_ike_address,
		     sizeof local_ike_address - 1, 0);

      policy_sa->transport->vtbl->get_dst (policy_sa->transport,
					   (struct sockaddr **)&sin, &fmt);
      my_inet_ntop4 (&(sin->sin_addr.s_addr), remote_ike_address,
		     sizeof remote_ike_address - 1, 0);

      switch (policy_isakmp_sa->exch_type)
	{
	case ISAKMP_EXCH_AGGRESSIVE:
	  phase_1 = "aggressive";
	  break;

	case ISAKMP_EXCH_ID_PROT:
	  phase_1 = "main";
	  break;
	}

      if (policy_isakmp_sa->initiator)
        {
	  id = policy_isakmp_sa->id_r;
	  id_sz = policy_isakmp_sa->id_r_len;
	}
      else
        {
	  id = policy_isakmp_sa->id_i;
	  id_sz = policy_isakmp_sa->id_i_len;
	}

      switch (id[0])
        {
	case IPSEC_ID_IPV4_ADDR:
	  remote_id_type = "IPv4 address";

	  net = decode_32 (id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ);
	  my_inet_ntop4 (&net, remote_id_addr_upper,
			 sizeof remote_id_addr_upper - 1, 1);
	  my_inet_ntop4 (&net, remote_id_addr_lower,
			 sizeof remote_id_addr_lower - 1, 1);
	  remote_id = strdup (remote_id_addr_upper);
	  if (!remote_id)
  	    {
	      log_error ("policy_callback: strdup (\"%s\") failed",
			 remote_id_addr_upper);
	      goto bad;
	    }
	  break;

	case IPSEC_ID_IPV4_RANGE:
	  remote_id_type = "IPv4 range";

	  net = decode_32 (id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ);
	  my_inet_ntop4 (&net, remote_id_addr_lower,
			 sizeof remote_id_addr_lower - 1, 1);
	  net = decode_32 (id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ + 4);
	  my_inet_ntop4 (&net, remote_id_addr_upper,
			 sizeof remote_id_addr_upper - 1, 1);
	  remote_id = calloc (strlen (remote_id_addr_upper)
			      + strlen (remote_id_addr_lower) + 2,
			      sizeof (char));
	  if (!remote_id)
	    {
	      log_error ("policy_callback: calloc (%d, %d) failed",
			 strlen (remote_id_addr_upper)
			 + strlen (remote_id_addr_lower) + 2,
			 sizeof (char));
	      goto bad;
	    }

	  strcpy (remote_id, remote_id_addr_lower);
	  remote_id[strlen (remote_id_addr_lower)] = '-';
	  strcpy (remote_id + strlen (remote_id_addr_lower) + 1,
		  remote_id_addr_upper);
	  break;

	case IPSEC_ID_IPV4_ADDR_SUBNET:
	  remote_id_type = "IPv4 subnet";

	  net = decode_32 (id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ);
	  subnet = decode_32 (id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ + 4);
	  net &= subnet;
	  my_inet_ntop4 (&net, remote_id_addr_lower,
			 sizeof remote_id_addr_lower - 1, 1);
	  net |= ~subnet;
	  my_inet_ntop4 (&net, remote_id_addr_upper,
			 sizeof remote_id_addr_upper - 1, 1);
	  remote_id = calloc (strlen (remote_id_addr_upper)
			      + strlen (remote_id_addr_lower) + 2,
			      sizeof (char));
	  if (!remote_id)
	    {
	      log_error ("policy_callback: calloc (%d, %d) failed",
			 strlen (remote_id_addr_upper)
			 + strlen (remote_id_addr_lower) + 2,
			 sizeof (char));
	      goto bad;
	    }

	  strcpy (remote_id, remote_id_addr_lower);
	  remote_id[strlen (remote_id_addr_lower)] = '-';
	  strcpy (remote_id + strlen (remote_id_addr_lower) + 1,
		  remote_id_addr_upper);
	  break;

	case IPSEC_ID_IPV6_ADDR:
	  /* XXX Not yet implemented.  */
	  remote_id_type = "IPv6 address";
	  break;

	case IPSEC_ID_IPV6_RANGE:
	  /* XXX Not yet implemented.  */
	  remote_id_type = "IPv6 range";
	  break;

	case IPSEC_ID_IPV6_ADDR_SUBNET:
	  /* XXX Not yet implemented.  */
	  remote_id_type = "IPv6 address";
	  break;

	case IPSEC_ID_FQDN:
	  remote_id_type = "FQDN";
	  remote_id = calloc (id_sz - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ + 1,
			      sizeof (char));
	  if (!remote_id)
	    {
	      log_error ("policy_callback: calloc (%d, %d) failed",
			 id_sz - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ + 1,
			 sizeof (char));
	      goto bad;
	    }
	  memcpy (remote_id, id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ,
		  id_sz - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ);
	  break;

	case IPSEC_ID_USER_FQDN:
	  remote_id_type = "User FQDN";
	  remote_id = calloc (id_sz - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ + 1,
			      sizeof (char));
	  if (!remote_id)
	    {
	      log_error ("policy_callback: calloc (%d, %d) failed",
			 id_sz - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ + 1,
			 sizeof (char));
	      goto bad;
	    }
	  memcpy (remote_id, id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ,
		  id_sz - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ);
	  break;

	case IPSEC_ID_DER_ASN1_DN: /* XXX -- not sure what's in this.  */
	  remote_id_type = "ASN1 DN";
	  break;

	case IPSEC_ID_DER_ASN1_GN: /* XXX -- not sure what's in this.  */
	  remote_id_type = "ASN1 GN";
	  break;

	case IPSEC_ID_KEY_ID:
	  remote_id_type = "Key ID";
	  remote_id
	    = calloc (2 * (id_sz - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ) + 1,
		      sizeof (char));
	  if (!remote_id)
	    {
	      log_error ("policy_callback: calloc (%d, %d) failed",
			 2 * (id_sz - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ) + 1,
			 sizeof (char));
	      goto bad;
	    }
          for (i = 0; i < id_sz - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ; i++)
	    {
	      remote_id[2 * i]
		= hextab[*(id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ) >> 4];
	      remote_id[2 * i + 1]
		= hextab[*(id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ) & 0xF];
	    }
	  break;

	default:
	  log_print ("policy_callback: unknown remote ID type %u", id[0]);
	  goto bad;
	}

      switch (id[1])
        {
	case IPPROTO_TCP:
	  remote_id_proto = "tcp";
	  break;

	case IPPROTO_UDP:
	  remote_id_proto = "udp";
	  break;

#ifdef IPPROTO_ETHERIP
	case IPPROTO_ETHERIP:
	  remote_id_proto = "etherip";
	  break;
#endif

 	default:
	  sprintf (remote_id_proto_num, "%2d", id[1]);
	  remote_id_proto = remote_id_proto_num;
	  break;
	}

      snprintf (remote_id_port, sizeof remote_id_port - 1, "%u",
		decode_16 (id + 2));

      if (policy_exchange->initiator)
        {
	  initiator = "yes";
	  idlocal = ie->id_ci;
	  idremote = ie->id_cr;
	  idlocalsz = ie->id_ci_sz;
	  idremotesz = ie->id_cr_sz;
        }
      else
        {
	  initiator = "no";
	  idlocal = ie->id_cr;
	  idremote = ie->id_ci;
	  idlocalsz = ie->id_cr_sz;
	  idremotesz = ie->id_ci_sz;
	}

      /* Initialize the ID variables.  */
      if (idremote)
        {
	  switch (GET_ISAKMP_ID_TYPE (idremote))
	    {
	    case IPSEC_ID_IPV4_ADDR:
	      remote_filter_type = "IPv4 address";

	      net = decode_32 (idremote + ISAKMP_ID_DATA_OFF);
	      my_inet_ntop4 (&net, remote_filter_addr_upper,
			     sizeof remote_filter_addr_upper - 1, 1);
	      my_inet_ntop4 (&net, remote_filter_addr_lower,
			     sizeof remote_filter_addr_lower - 1, 1);
	      remote_filter = strdup (remote_filter_addr_upper);
	      if (!remote_filter)
	        {
		  log_error ("policy_callback: strdup (\"%s\") failed",
			     remote_filter_addr_upper);
		  goto bad;
		}
	      break;

	    case IPSEC_ID_IPV4_RANGE:
	      remote_filter_type = "IPv4 range";

	      net = decode_32 (idremote + ISAKMP_ID_DATA_OFF);
	      my_inet_ntop4 (&net, remote_filter_addr_lower,
			     sizeof remote_filter_addr_lower - 1, 1);
	      net = decode_32 (idremote + ISAKMP_ID_DATA_OFF + 4);
	      my_inet_ntop4 (&net, remote_filter_addr_upper,
			     sizeof remote_filter_addr_upper - 1, 1);
	      remote_filter = calloc (strlen (remote_filter_addr_upper)
				      + strlen (remote_filter_addr_lower) + 2,
				      sizeof (char));
	      if (!remote_filter)
	        {
		  log_error ("policy_callback: calloc (%d, %d) failed",
			     strlen (remote_filter_addr_upper)
			     + strlen (remote_filter_addr_lower) + 2,
			     sizeof (char));
		  goto bad;
		}
	      strcpy (remote_filter, remote_filter_addr_lower);
	      remote_filter[strlen (remote_filter_addr_lower)] = '-';
	      strcpy (remote_filter + strlen (remote_filter_addr_lower) + 1,
		      remote_filter_addr_upper);
	      break;

	    case IPSEC_ID_IPV4_ADDR_SUBNET:
	      remote_filter_type = "IPv4 subnet";

	      net = decode_32 (idremote + ISAKMP_ID_DATA_OFF);
	      subnet = decode_32 (idremote + ISAKMP_ID_DATA_OFF + 4);
	      net &= subnet;
	      my_inet_ntop4 (&net, remote_filter_addr_lower,
			     sizeof remote_filter_addr_lower - 1, 1);
	      net |= ~subnet;
	      my_inet_ntop4 (&net, remote_filter_addr_upper,
			     sizeof remote_filter_addr_upper - 1, 1);
	      remote_filter = calloc (strlen (remote_filter_addr_upper)
				      + strlen (remote_filter_addr_lower) + 2,
				      sizeof (char));
	      if (!remote_filter)
	        {
		  log_error ("policy_callback: calloc (%d, %d) failed",
			     strlen (remote_filter_addr_upper)
			     + strlen (remote_filter_addr_lower) + 2,
			     sizeof (char));
		  goto bad;
		}
	      strcpy (remote_filter, remote_filter_addr_lower);
	      remote_filter[strlen (remote_filter_addr_lower)] = '-';
	      strcpy (remote_filter + strlen (remote_filter_addr_lower) + 1,
		      remote_filter_addr_upper);
	      break;

	    case IPSEC_ID_IPV6_ADDR:
	      /* XXX Not yet implemented.  */
	      remote_filter_type = "IPv6 address";
	      break;

	    case IPSEC_ID_IPV6_RANGE:
	      /* XXX Not yet implemented.  */
	      remote_filter_type = "IPv6 range";
	      break;

	    case IPSEC_ID_IPV6_ADDR_SUBNET:
	      /* XXX Not yet implemented.  */
	      remote_filter_type = "IPv6 address";
	      break;

	    case IPSEC_ID_FQDN:
	      remote_filter_type = "FQDN";
	      remote_filter = calloc (idremotesz - ISAKMP_ID_DATA_OFF + 1,
				      sizeof (char));
	      if (!remote_filter)
	        {
		  log_error ("policy_callback: calloc (%d, %d) failed",
			     idremotesz - ISAKMP_ID_DATA_OFF + 1,
			     sizeof (char));
		  goto bad;
		}
	      memcpy (remote_filter, idremote + ISAKMP_ID_DATA_OFF,
		      idremotesz);
	      break;

	    case IPSEC_ID_USER_FQDN:
	      remote_filter_type = "User FQDN";
	      remote_filter = calloc (idremotesz - ISAKMP_ID_DATA_OFF + 1,
				      sizeof (char));
	      if (!remote_filter)
	        {
		  log_error ("policy_callback: calloc (%d, %d) failed",
			     idremotesz - ISAKMP_ID_DATA_OFF + 1,
			     sizeof (char));
		  goto bad;
		}
	      memcpy (remote_filter, idremote + ISAKMP_ID_DATA_OFF,
		      idremotesz);
	      break;

	    case IPSEC_ID_DER_ASN1_DN: /* XXX -- not sure what's in this.  */
	      remote_filter_type = "ASN1 DN";
	      break;

	    case IPSEC_ID_DER_ASN1_GN: /* XXX -- not sure what's in this.  */
	      remote_filter_type = "ASN1 GN";
	      break;

	    case IPSEC_ID_KEY_ID:
	      remote_filter_type = "Key ID";
	      remote_filter
		= calloc (2 * (idremotesz - ISAKMP_ID_DATA_OFF) + 1,
			  sizeof (char));
	      if (!remote_filter)
	        {
		  log_error ("policy_callback: calloc (%d, %d) failed",
			     2 * (idremotesz - ISAKMP_ID_DATA_OFF) + 1,
			     sizeof (char));
		  goto bad;
	        }
              for (i = 0; i < idremotesz - ISAKMP_ID_DATA_OFF; i++)
	        {
		  remote_filter[2 * i]
		    = hextab[*(idremote + ISAKMP_ID_DATA_OFF) >> 4];
		  remote_filter[2 * i + 1]
		    = hextab[*(idremote + ISAKMP_ID_DATA_OFF) & 0xF];
	        }
	      break;

	    default:
	      log_print ("policy_callback: unknown Remote ID type %u",
			 GET_ISAKMP_ID_TYPE (idremote));
	      goto bad;
	    }

	  switch (idremote[ISAKMP_GEN_SZ + 1])
	    {
	    case IPPROTO_TCP:
	      remote_filter_proto = "tcp";
	      break;

	    case IPPROTO_UDP:
	      remote_filter_proto = "udp";
	      break;

#ifdef IPPROTO_ETHERIP
	    case IPPROTO_ETHERIP:
	      remote_filter_proto = "etherip";
	      break;
#endif

 	    default:
	      sprintf (remote_filter_proto_num, "%2d",
		       idremote[ISAKMP_GEN_SZ + 1]);
	      remote_filter_proto = remote_filter_proto_num;
	      break;
	    }

	  snprintf (remote_filter_port, sizeof remote_filter_port - 1,
		    "%u", decode_16 (idremote + ISAKMP_GEN_SZ + 2));
	}
      else
        {
	  policy_sa->transport->vtbl->get_dst (policy_sa->transport,
					       (struct sockaddr **) &sin,
					       &fmt);
	  remote_filter_type = "IPv4 address";

	  my_inet_ntop4 (&(sin->sin_addr.s_addr), remote_filter_addr_upper,
			 sizeof remote_filter_addr_upper - 1, 0);
	  my_inet_ntop4 (&(sin->sin_addr.s_addr), remote_filter_addr_lower,
			 sizeof remote_filter_addr_lower - 1, 0);
	  remote_filter = strdup (remote_filter_addr_upper);
	  if (!remote_filter)
	    {
	      log_error ("policy_callback: strdup (\"%s\") failed",
			 remote_filter_addr_upper);
	      goto bad;
	    }
	}

      if (idlocal)
        {
	  switch (GET_ISAKMP_ID_TYPE (idlocal))
	    {
	    case IPSEC_ID_IPV4_ADDR:
	      local_filter_type = "IPv4 address";

	      net = decode_32 (idlocal + ISAKMP_ID_DATA_OFF);
	      my_inet_ntop4 (&net, local_filter_addr_upper,
			     sizeof local_filter_addr_upper - 1, 1);
	      my_inet_ntop4 (&net, local_filter_addr_lower,
			     sizeof local_filter_addr_upper - 1, 1);
	      local_filter = strdup (local_filter_addr_upper);
	      if (!local_filter)
	        {
		  log_error ("policy_callback: strdup (\"%s\") failed",
			     local_filter_addr_upper);
		  goto bad;
		}
	      break;

	    case IPSEC_ID_IPV4_RANGE:
	      local_filter_type = "IPv4 range";

	      net = decode_32 (idlocal + ISAKMP_ID_DATA_OFF);
	      my_inet_ntop4 (&net, local_filter_addr_lower,
			     sizeof local_filter_addr_lower - 1, 1);
	      net = decode_32 (idlocal + ISAKMP_ID_DATA_OFF + 4);
	      my_inet_ntop4 (&net, local_filter_addr_upper,
			     sizeof local_filter_addr_upper - 1, 1);
	      local_filter = calloc (strlen (local_filter_addr_upper)
				     + strlen (local_filter_addr_lower) + 2,
				     sizeof (char));
	      if (!local_filter)
	        {
		  log_error ("policy_callback: calloc (%d, %d) failed",
			     strlen (local_filter_addr_upper)
			     + strlen (local_filter_addr_lower) + 2,
			     sizeof (char));
		  goto bad;
		}
	      strcpy (local_filter, local_filter_addr_lower);
	      local_filter[strlen (local_filter_addr_lower)] = '-';
	      strcpy (local_filter + strlen (local_filter_addr_lower) + 1,
		      local_filter_addr_upper);
	      break;

	    case IPSEC_ID_IPV4_ADDR_SUBNET:
	      local_filter_type = "IPv4 subnet";

	      net = decode_32 (idlocal + ISAKMP_ID_DATA_OFF);
	      subnet = decode_32 (idlocal + ISAKMP_ID_DATA_OFF + 4);
	      net &= subnet;
	      my_inet_ntop4 (&net, local_filter_addr_lower,
			     sizeof local_filter_addr_lower - 1, 1);
	      net |= ~subnet;
	      my_inet_ntop4 (&net, local_filter_addr_upper,
			     sizeof local_filter_addr_upper - 1, 1);
	      local_filter = calloc (strlen (local_filter_addr_upper)
				     + strlen (local_filter_addr_lower) + 2,
				     sizeof (char));
	      if (!local_filter)
	        {
		  log_error ("policy_callback: calloc (%d, %d) failed",
			     strlen (local_filter_addr_upper)
			     + strlen (local_filter_addr_lower) + 2,
			     sizeof (char));
		  goto bad;
		}
	      strcpy (local_filter, local_filter_addr_lower);
	      local_filter[strlen (local_filter_addr_lower)] = '-';
	      strcpy (local_filter + strlen (local_filter_addr_lower) + 1,
		      local_filter_addr_upper);
	      break;

	    case IPSEC_ID_IPV6_ADDR:
	      /* XXX Not yet implemented.  */
	      local_filter_type = "IPv6 address";
	      break;

	    case IPSEC_ID_IPV6_RANGE:
	      /* XXX Not yet implemented.  */
	      local_filter_type = "IPv6 range";
	      break;

	    case IPSEC_ID_IPV6_ADDR_SUBNET:
	      /* XXX Not yet implemented.  */
	      local_filter_type = "IPv6 address";
	      break;

	    case IPSEC_ID_FQDN:
	      local_filter_type = "FQDN";
	      local_filter = calloc (idlocalsz - ISAKMP_ID_DATA_OFF + 1,
				     sizeof (char));
	      if (!local_filter)
	        {
		  log_error ("policy_callback: calloc (%d, %d) failed",
			     idlocalsz - ISAKMP_ID_DATA_OFF + 1,
			     sizeof (char));
		  goto bad;
		}
	      memcpy (local_filter, idlocal + ISAKMP_ID_DATA_OFF,
		      idlocalsz);
	      break;

	    case IPSEC_ID_USER_FQDN:
	      local_filter_type = "User FQDN";
	      local_filter = calloc (idlocalsz - ISAKMP_ID_DATA_OFF + 1,
				     sizeof (char));
	      if (!local_filter)
	        {
		  log_error ("policy_callback: calloc (%d, %d) failed",
			     idlocalsz - ISAKMP_ID_DATA_OFF + 1,
			     sizeof (char));
		  goto bad;
		}
	      memcpy (local_filter, idlocal + ISAKMP_ID_DATA_OFF,
		      idlocalsz);
	      break;

	    case IPSEC_ID_DER_ASN1_DN: /* XXX -- not sure what's in this.  */
	      local_filter_type = "ASN1 DN";
	      break;

	    case IPSEC_ID_DER_ASN1_GN: /* XXX -- not sure what's in this.  */
	      local_filter_type = "ASN1 GN";
	      break;

	    case IPSEC_ID_KEY_ID:
	      local_filter_type = "Key ID";
	      local_filter = calloc (2 * (idlocalsz - ISAKMP_ID_DATA_OFF) + 1,
				     sizeof (char));
	      if (!local_filter)
	        {
		  log_error ("policy_callback: calloc (%d, %d) failed",
			     2 * (idlocalsz - ISAKMP_ID_DATA_OFF) + 1,
			     sizeof (char));
		  goto bad;
	        }
              for (i = 0; i < idremotesz - ISAKMP_ID_DATA_OFF; i++)
	        {
		  local_filter[2 * i]
		    = hextab[*(idlocal + ISAKMP_ID_DATA_OFF) >> 4];
		  local_filter[2 * i + 1]
		    = hextab[*(idlocal + ISAKMP_ID_DATA_OFF) & 0xF];
	        }
	      break;

	    default:
	      log_print ("policy_callback: unknown Local ID type %u",
			 GET_ISAKMP_ID_TYPE (idlocal));
	      goto bad;
	    }

	  switch (idlocal[ISAKMP_GEN_SZ + 1])
	    {
	    case IPPROTO_TCP:
	      local_filter_proto = "tcp";
	      break;

	    case IPPROTO_UDP:
	      local_filter_proto = "udp";
	      break;

#ifdef IPPROTO_ETHERIP
	    case IPPROTO_ETHERIP:
	      local_filter_proto = "etherip";
	      break;
#endif

 	    default:
	      sprintf (local_filter_proto_num, "%2d",
		       idlocal[ISAKMP_GEN_SZ + 1]);
	      local_filter_proto = local_filter_proto_num;
	      break;
	    }

	  snprintf (local_filter_port, sizeof local_filter_port - 1,
		    "%u", decode_16 (idlocal + ISAKMP_GEN_SZ + 2));
	}
      else
        {
	  policy_sa->transport->vtbl->get_src (policy_sa->transport,
					       (struct sockaddr **)&sin,
					       &fmt);

	  local_filter_type = "IPv4 address";

	  my_inet_ntop4 (&(sin->sin_addr.s_addr), local_filter_addr_upper,
			 sizeof local_filter_addr_upper - 1, 0);
	  my_inet_ntop4 (&(sin->sin_addr.s_addr), local_filter_addr_lower,
			 sizeof local_filter_addr_lower - 1, 0);
	  local_filter = strdup (local_filter_addr_upper);
	  if (!local_filter)
	    {
	      log_error ("policy_callback: strdup (\"%s\") failed",
			 local_filter_addr_upper);
	      goto bad;
	    }
        }

      LOG_DBG ((LOG_POLICY, 80, "Policy context (action attributes):"));
      LOG_DBG ((LOG_POLICY, 80, "esp_present == %s", esp_present));
      LOG_DBG ((LOG_POLICY, 80, "ah_present == %s", ah_present));
      LOG_DBG ((LOG_POLICY, 80, "comp_present == %s", comp_present));
      LOG_DBG ((LOG_POLICY, 80, "ah_hash_alg == %s", ah_hash_alg));
      LOG_DBG ((LOG_POLICY, 80, "esp_enc_alg == %s", esp_enc_alg));
      LOG_DBG ((LOG_POLICY, 80, "comp_alg == %s", comp_alg));
      LOG_DBG ((LOG_POLICY, 80, "ah_auth_alg == %s", ah_auth_alg));
      LOG_DBG ((LOG_POLICY, 80, "esp_auth_alg == %s", esp_auth_alg));
      LOG_DBG ((LOG_POLICY, 80, "ah_life_seconds == %s", ah_life_seconds));
      LOG_DBG ((LOG_POLICY, 80, "ah_life_kbytes == %s", ah_life_kbytes));
      LOG_DBG ((LOG_POLICY, 80, "esp_life_seconds == %s", esp_life_seconds));
      LOG_DBG ((LOG_POLICY, 80, "esp_life_kbytes == %s", esp_life_kbytes));
      LOG_DBG ((LOG_POLICY, 80, "comp_life_seconds == %s", comp_life_seconds));
      LOG_DBG ((LOG_POLICY, 80, "comp_life_kbytes == %s", comp_life_kbytes));
      LOG_DBG ((LOG_POLICY, 80, "ah_encapsulation == %s", ah_encapsulation));
      LOG_DBG ((LOG_POLICY, 80, "esp_encapsulation == %s", esp_encapsulation));
      LOG_DBG ((LOG_POLICY, 80, "comp_encapsulation == %s",
		comp_encapsulation));
      LOG_DBG ((LOG_POLICY, 80, "comp_dict_size == %s", comp_dict_size));
      LOG_DBG ((LOG_POLICY, 80, "comp_private_alg == %s", comp_private_alg));
      LOG_DBG ((LOG_POLICY, 80, "ah_key_length == %s", ah_key_length));
      LOG_DBG ((LOG_POLICY, 80, "ah_key_rounds == %s", ah_key_rounds));
      LOG_DBG ((LOG_POLICY, 80, "esp_key_length == %s", esp_key_length));
      LOG_DBG ((LOG_POLICY, 80, "esp_key_rounds == %s", esp_key_rounds));
      LOG_DBG ((LOG_POLICY, 80, "ah_group_desc == %s", ah_group_desc));
      LOG_DBG ((LOG_POLICY, 80, "esp_group_desc == %s", esp_group_desc));
      LOG_DBG ((LOG_POLICY, 80, "comp_group_desc == %s", comp_group_desc));
      LOG_DBG ((LOG_POLICY, 80, "remote_filter_type == %s",
		remote_filter_type));
      LOG_DBG ((LOG_POLICY, 80, "remote_filter_addr_upper == %s",
		remote_filter_addr_upper));
      LOG_DBG ((LOG_POLICY, 80, "remote_filter_addr_lower == %s",
		remote_filter_addr_lower));
      LOG_DBG ((LOG_POLICY, 80, "remote_filter == %s",
		(remote_filter ? remote_filter : "")));
      LOG_DBG ((LOG_POLICY, 80, "remote_filter_port == %s",
		remote_filter_port));
      LOG_DBG ((LOG_POLICY, 80, "remote_filter_proto == %s",
		remote_filter_proto));
      LOG_DBG ((LOG_POLICY, 80, "local_filter_type == %s", local_filter_type));
      LOG_DBG ((LOG_POLICY, 80, "local_filter_addr_upper == %s",
		local_filter_addr_upper));
      LOG_DBG ((LOG_POLICY, 80, "local_filter_addr_lower == %s",
		local_filter_addr_lower));
      LOG_DBG ((LOG_POLICY, 80, "local_filter == %s",
		(local_filter ? local_filter : "")));
      LOG_DBG ((LOG_POLICY, 80, "local_filter_port == %s", local_filter_port));
      LOG_DBG ((LOG_POLICY, 80, "local_filter_proto == %s",
		local_filter_proto));
      LOG_DBG ((LOG_POLICY, 80, "remote_id_type == %s", remote_id_type));
      LOG_DBG ((LOG_POLICY, 80, "remote_id_addr_upper == %s",
		remote_id_addr_upper));
      LOG_DBG ((LOG_POLICY, 80, "remote_id_addr_lower == %s",
		remote_id_addr_lower));
      LOG_DBG ((LOG_POLICY, 80, "remote_id == %s",
		(remote_id ? remote_id : "")));
      LOG_DBG ((LOG_POLICY, 80, "remote_id_port == %s", remote_id_port));
      LOG_DBG ((LOG_POLICY, 80, "remote_id_proto == %s", remote_id_proto));
      LOG_DBG ((LOG_POLICY, 80, "remote_negotiation_address == %s",
		remote_ike_address));
      LOG_DBG ((LOG_POLICY, 80, "local_negotiation_address == %s",
		local_ike_address));
      LOG_DBG ((LOG_POLICY, 80, "pfs == %s", pfs));
      LOG_DBG ((LOG_POLICY, 80, "initiator == %s", initiator));
      LOG_DBG ((LOG_POLICY, 80, "phase1_group_desc == %s", phase1_group));

      /* Unset dirty now.  */
      dirty = 0;
    }

  if (strcmp (name, "phase_1") == 0)
    return phase_1;

  if (strcmp (name, "GMTTimeOfDay") == 0)
    {
      tt = time ((time_t) NULL);
      strftime (mytimeofday, 14, "%G%m%d%H%M%S", gmtime (&tt));
      return mytimeofday;
    }

  if (strcmp (name, "LocalTimeOfDay") == 0)
    {
      tt = time ((time_t) NULL);
      strftime (mytimeofday, 14, "%G%m%d%H%M%S", localtime (&tt));
      return mytimeofday;
    }

  if (strcmp (name, "initiator") == 0)
    return initiator;

  if (strcmp (name, "pfs") == 0)
    return pfs;

  if (strcmp (name, "app_domain") == 0)
    return "IPsec policy";

  if (strcmp (name, "doi") == 0)
    return "ipsec";

  if (strcmp (name, "esp_present") == 0)
    return esp_present;

  if (strcmp (name, "ah_present") == 0)
    return ah_present;

  if (strcmp (name, "comp_present") == 0)
    return comp_present;

  if (strcmp (name, "ah_hash_alg") == 0)
    return ah_hash_alg;

  if (strcmp (name, "ah_auth_alg") == 0)
    return ah_auth_alg;

  if (strcmp (name, "esp_auth_alg") == 0)
    return esp_auth_alg;

  if (strcmp (name, "esp_enc_alg") == 0)
    return esp_enc_alg;

  if (strcmp (name, "comp_alg") == 0)
    return comp_alg;

  if (strcmp (name, "ah_life_kbytes") == 0)
    return ah_life_kbytes;

  if (strcmp (name, "ah_life_seconds") == 0)
    return ah_life_seconds;

  if (strcmp (name, "esp_life_kbytes") == 0)
    return esp_life_kbytes;

  if (strcmp (name, "esp_life_seconds") == 0)
    return esp_life_seconds;

  if (strcmp (name, "comp_life_kbytes") == 0)
    return comp_life_kbytes;

  if (strcmp (name, "comp_life_seconds") == 0)
    return comp_life_seconds;

  if (strcmp (name, "ah_encapsulation") == 0)
    return ah_encapsulation;

  if (strcmp (name, "esp_encapsulation") == 0)
    return esp_encapsulation;

  if (strcmp (name, "comp_encapsulation") == 0)
    return comp_encapsulation;

  if (strcmp (name, "ah_key_length") == 0)
    return ah_key_length;

  if (strcmp (name, "ah_key_rounds") == 0)
    return ah_key_rounds;

  if (strcmp (name, "esp_key_length") == 0)
    return esp_key_length;

  if (strcmp (name, "esp_key_rounds") == 0)
    return esp_key_rounds;

  if (strcmp (name, "comp_dict_size") == 0)
    return comp_dict_size;

  if (strcmp (name, "comp_private_alg") == 0)
    return comp_private_alg;

  if (strcmp (name, "remote_filter_type") == 0)
    return remote_filter_type;

  if (strcmp (name, "remote_filter") == 0)
    return (remote_filter ? remote_filter : "");

  if (strcmp (name, "remote_filter_addr_upper") == 0)
    return remote_filter_addr_upper;

  if (strcmp (name, "remote_filter_addr_lower") == 0)
    return remote_filter_addr_lower;

  if (strcmp (name, "remote_filter_port") == 0)
    return remote_filter_port;

  if (strcmp (name, "remote_filter_proto") == 0)
    return remote_filter_proto;

  if (strcmp (name, "local_filter_type") == 0)
    return local_filter_type;

  if (strcmp (name, "local_filter") == 0)
    return (local_filter ? local_filter : "");

  if (strcmp (name, "local_filter_addr_upper") == 0)
    return local_filter_addr_upper;

  if (strcmp (name, "local_filter_addr_lower") == 0)
    return local_filter_addr_lower;

  if (strcmp (name, "local_filter_port") == 0)
    return local_filter_port;

  if (strcmp (name, "local_filter_proto") == 0)
    return local_filter_proto;

  if (strcmp (name, "remote_ike_address") == 0)
    return remote_ike_address;

  if (strcmp (name, "remote_negotiation_address") == 0)
    return remote_ike_address;

  if (strcmp (name, "local_ike_address") == 0)
    return local_ike_address;

  if (strcmp (name, "local_negotiation_address") == 0)
    return local_ike_address;

  if (strcmp (name, "remote_id_type") == 0)
    return remote_id_type;

  if (strcmp (name, "remote_id") == 0)
    return (remote_id ? remote_id : "");

  if (strcmp (name, "remote_id_addr_upper") == 0)
    return remote_id_addr_upper;

  if (strcmp (name, "remote_id_addr_lower") == 0)
    return remote_id_addr_lower;

  if (strcmp (name, "remote_id_port") == 0)
    return remote_id_port;

  if (strcmp (name, "remote_id_proto") == 0)
    return remote_id_proto;

  if (strcmp (name, "phase1_group_desc") == 0)
    return phase1_group;

  return "";

 bad:
  policy_callback (KEYNOTE_CALLBACK_INITIALIZE);
  return "";
}

void
policy_init (void)
{
  char *ptr, *policy_file;
  char **asserts;
  off_t sz;
  int fd, len, i;

  LOG_DBG ((LOG_POLICY, 30, "policy_init: initializing"));

#if defined (HAVE_DLOPEN) && !defined (USE_KEYNOTE)
  if (!dyn_load (libkeynote_script))
    return;
#endif

  /* If there exists a session already, release all its resources.  */
  if (keynote_sessid != -1)
    LK (kn_close, (keynote_sessid));

  /* Initialize a session.  */
  keynote_sessid = LK (kn_init, ());
  if (keynote_sessid == -1)
    log_fatal ("policy_init: kn_init () failed");

  /* Get policy file from configuration.  */
  policy_file = conf_get_str ("General", "Policy-file");
  if (!policy_file)
    policy_file = POLICY_FILE_DEFAULT;

  /* Check file modes and collect file size */
  if (check_file_secrecy (policy_file, &sz))
    log_fatal ("policy_init: cannot read %s", policy_file);

  /* Open policy file.  */
  fd = open (policy_file, O_RDONLY);
  if (fd == -1)
    log_fatal ("policy_init: open (\"%s\", O_RDONLY) failed", policy_file);

  /* Allocate memory to keep policies.  */
  ptr = calloc (sz + 1, sizeof (char));
  if (!ptr)
    log_fatal ("policy_init: calloc (%d, %d) failed", sz + 1,
	       sizeof (char));

  /* Just in case there are short reads...  */
  for (len = 0; len < sz; len += i)
    {
      i = read (fd, ptr + len, sz - len);
      if (i == -1)
	log_fatal ("policy_init: read (%d, %p, %d) failed", fd, ptr + len,
		   sz - len);
    }

  /* We're done with this.  */
  close (fd);

  /* Parse buffer, break up into individual policies.  */
  asserts = LK (kn_read_asserts, (ptr, sz, &i));

  /* Begone!  */
  free (ptr);

  /* Add each individual policy in the session.  */
  for (fd = 0; fd < i; fd++)
    {
      if (LK (kn_add_assertion, (keynote_sessid, asserts[fd],
				 strlen (asserts[fd]), ASSERT_FLAG_LOCAL))
	  == -1)
        log_print ("policy_init: "
		   "kn_add_assertion (%d, %p, %d, ASSERT_FLAG_LOCAL) failed",
                   keynote_sessid, asserts[fd], strlen (asserts[fd]));
    }

  /* Cleanup */
  if (keynote_policy_asserts)
    {
      for (fd = 0; fd < keynote_policy_asserts_num; fd++)
        if (keynote_policy_asserts && keynote_policy_asserts[fd])
          free (keynote_policy_asserts[fd]);

      free (keynote_policy_asserts);
    }

  keynote_policy_asserts = asserts;
  keynote_policy_asserts_num = i;
}

/* Nothing needed for initialization */
int
keynote_cert_init (void)
{
  return 1;
}

/* Just copy and return.  */
void *
keynote_cert_get (u_int8_t *data, u_int32_t len)
{
  char *foo = calloc (len + 1, sizeof (char));

  if (foo == NULL)
    return NULL;

  memcpy (foo, data, len);
  return foo;
}

/*
 * We just verify the signature on the credentials.
 * On signature failure, just drop the whole payload.
 */
int
keynote_cert_validate (void *scert)
{
  char **foo;
  int num, i;

  if (scert == NULL)
    return 0;

  foo = LK (kn_read_asserts, ((char *) scert, strlen ((char *) scert),
			      &num));
  if (foo == NULL)
    return 0;

  for (i = 0; i < num; i++)
    {
      if (LK (kn_verify_assertion, (scert, strlen ((char *) scert)))
	  != SIGRESULT_TRUE)
        {
	  for (; i < num; i++)
	    free (foo[i]);
	  free (foo);
	  return 0;
	}

      free (foo[i]);
    }

  free (foo);
  return 1;
}

/* Add received credentials.  */
int
keynote_cert_insert (int sid, void *scert)
{
  char **foo;
  int num;

  if (scert == NULL)
    return 0;

  foo = LK (kn_read_asserts, ((char *) scert, strlen ((char *) scert),
			      &num));
  if (foo == NULL)
    return 0;

  while (num--)
    LK (kn_add_assertion, (sid, foo[num], strlen (foo[num]), 0));

  return 1;
}

/* Just regular memory free.  */
void
keynote_cert_free (void *cert)
{
  free (cert);
}

/* Verify that the key given to us is valid.  */
int
keynote_certreq_validate (u_int8_t *data, u_int32_t len)
{
  struct keynote_deckey dc;
  int err = 1;
  char *dat;

  dat = calloc (len + 1, sizeof (char));
  if (!dat)
    {
      log_error ("keynote_certreq_validate: calloc (%d, %d) failed", len + 1,
		 sizeof (char));
	return 0;
    }

  memcpy (dat, data, len);

  if (LK (kn_decode_key, (&dc, dat, KEYNOTE_PUBLIC_KEY)) != 0)
    err = 0;
  else
    LK (kn_free_key, (&dc));

  free (dat);

  return err;
}

/* Beats me what we should be doing with this.  */
void *
keynote_certreq_decode (u_int8_t *data, u_int32_t len)
{
  /* XXX */
  return NULL;
}

void
keynote_free_aca (void *blob)
{
  /* XXX */
}

int
keynote_cert_obtain (u_int8_t *id, size_t id_len, void *data, u_int8_t **cert,
		     u_int32_t *certlen)
{
  char *dirname, *file;
  struct stat sb;
  int idtype, fd, len;

  if (!id)
    {
      log_print ("keynote_cert_obtain: ID is missing");
      return 0;
    }

  /* Get type of ID.  */
  idtype = id[0];
  id += ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ;
  id_len -= ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ;

  dirname = conf_get_str ("KeyNote", "Credential-directory");
  if (!dirname)
    {
      LOG_DBG ((LOG_POLICY, 30,
		"keynote_cert_obtain: no Credential-directory"));
      return 0;
    }

  len = strlen (dirname) + strlen (CREDENTIAL_FILE) + 3;

  switch (idtype)
    {
    case IPSEC_ID_IPV4_ADDR:
      {
	struct in_addr in;

	file = calloc (len + 15, sizeof (char));
	if (file == NULL)
	  {
	    log_error ("keynote_cert_obtain: failed to allocate %d bytes",
		       len + 15);
	    return 0;
	  }

	memcpy (&in, id, sizeof in);
	sprintf (file, "%s/%s/%s", dirname, inet_ntoa (in), CREDENTIAL_FILE);
	break;
      }

    case IPSEC_ID_FQDN:
    case IPSEC_ID_USER_FQDN:
      {
        file = calloc (len + id_len, sizeof (char));
	if (file == NULL)
	  {
	    log_error ("keynote_cert_obtain: failed to allocate %d bytes",
		       len + id_len);
	    return 0;
	  }

	sprintf (file, "%s/", dirname);
	memcpy (file + strlen (file), id, id_len);
	sprintf (file + strlen (dirname) + 1 + id_len, "/%s", CREDENTIAL_FILE);
	break;
      }

    default:
      return 0;
    }

  if (stat (file, &sb) < 0)
    {
      LOG_DBG ((LOG_POLICY, 30, "keynote_cert_obtain: failed to stat \"%s\"",
		file));
      free (file);
      return 0;
    }

  *cert = calloc (sb.st_size, sizeof (char));
  if (*cert == NULL)
    {
      log_error ("keynote_cert_obtain: failed to allocate %d bytes",
		 sb.st_size);
      free (file);
      return 0;
    }

  fd = open (file, O_RDONLY, 0);
  if (fd < 0)
    {
      LOG_DBG ((LOG_POLICY, 30, "keynote_cert_obtain: failed to open \"%s\"",
		file));
      free (file);
      return 0;
    }

  if (read (fd, *cert, sb.st_size) != sb.st_size)
    {
      LOG_DBG ((LOG_POLICY, 30, "keynote_cert_obtain: failed to read %d "
		"bytes from \"%s\"", sb.st_size, file));
      free (file);
      close (fd);
      return 0;
    }

  close (fd);
  free (file);
  *certlen = sb.st_size;
  return 1;
}

/* This should never be called.  */
int
keynote_cert_get_subjects (void *scert, int *n, u_int8_t ***id,
			   u_int32_t **id_len)
{
  return 0;
}

/* Get the authorizer key.  */
int
keynote_cert_get_key (void *scert, void *keyp)
{
  struct keynote_keylist *kl;
  int sid, num;
  char **foo;

  foo = LK (kn_read_asserts, ((char *)scert, strlen ((char *)scert), &num));
  if (foo == NULL || num == 0)
    return 0;

  sid = LK (kn_add_assertion, (keynote_sessid, foo[num - 1],
			       strlen (scert), 0));
  while (num--)
    free (foo[num]);
  free (foo);

  if (sid == -1)
    return 0;

  *(RSA **)keyp = NULL;

  kl = LK (kn_get_licensees, (keynote_sessid, sid));
  while (kl)
    {
      if (kl->key_alg == KEYNOTE_ALGORITHM_RSA)
	{
	  *(RSA **)keyp = LC (RSAPublicKey_dup, (kl->key_key));
	  break;
	}

      kl = kl->key_next;
    }

  LK (kn_remove_assertion, (keynote_sessid, sid));
  return *(RSA **)keyp == NULL ? 0 : 1;
}