OpenBSD-4.6/sbin/isakmpd/policy.c

Compare this file to the similar file:
Show the results in this format:

/* $OpenBSD: policy.c,v 1.91 2007/08/05 09:43:09 tom 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.
 * Copyright (c) 2001 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.
 *
 * 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 <netdb.h>

#include "conf.h"
#include "exchange.h"
#include "ipsec.h"
#include "isakmp_doi.h"
#include "sa.h"
#include "transport.h"
#include "log.h"
#include "message.h"
#include "monitor.h"
#include "util.h"
#include "policy.h"
#include "x509.h"

char          **policy_asserts = NULL;
int		ignore_policy = 0;
int             policy_asserts_num = 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;
	int		len;

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

	len = snprintf(tmp, sizeof tmp, fmt, ((u_int8_t *)&src2)[0],
	    ((u_int8_t *)&src2)[1], ((u_int8_t *)&src2)[2],
	    ((u_int8_t *)&src2)[3]);
	if (len == -1 || len > (int)size) {
		errno = ENOSPC;
		return 0;
	}
	strlcpy(dst, tmp, size);
	return dst;
}

static const char *
my_inet_ntop6(const unsigned char *src, char *dst, size_t size)
{
	static const char fmt[] =
	    "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x";
	char	tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"];
	int	len;

	len = snprintf(tmp, sizeof tmp, fmt, src[0], src[1], src[2], src[3],
	    src[4], src[5], src[6], src[7], src[8], src[9], src[10], src[11],
	    src[12], src[13], src[14], src[15]);
	if (len == -1 || len > (int)size) {
		errno = ENOSPC;
		return 0;
	}
	strlcpy(dst, tmp, size);
	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 *sin;
	struct ipsec_exch *ie;
	struct ipsec_sa *is;
	size_t          i;
	int             fmt, lifetype = 0;
	in_addr_t       net, subnet;
	u_int16_t       len, type;
	time_t          tt;
	char           *addr;
	static char     mytimeofday[15];

	/* We use all these as a cache.  */
#define PMAX 32
	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[PMAX], ah_life_seconds[PMAX];
	static char     esp_life_kbytes[PMAX], esp_life_seconds[PMAX];
	static char     comp_life_kbytes[PMAX];
	static char    *ah_ecn, *esp_ecn, *comp_ecn;
	static char     comp_life_seconds[PMAX], *ah_encapsulation;
	static char    *esp_encapsulation, *comp_encapsulation;
	static char     ah_key_length[PMAX], esp_key_length[PMAX];
	static char     ah_key_rounds[PMAX], esp_key_rounds[PMAX];
	static char	comp_dict_size[PMAX], comp_private_alg[PMAX];
	static char    *remote_filter_type, *local_filter_type;
	static char     remote_filter_addr_upper[NI_MAXHOST];
	static char     remote_filter_addr_lower[NI_MAXHOST];
	static char     local_filter_addr_upper[NI_MAXHOST];
	static char     local_filter_addr_lower[NI_MAXHOST];
	static char     ah_group_desc[PMAX], esp_group_desc[PMAX];
	static char	comp_group_desc[PMAX], remote_ike_address[NI_MAXHOST];
	static char     local_ike_address[NI_MAXHOST];
	static char    *remote_id_type, remote_id_addr_upper[NI_MAXHOST];
	static char    *phase_1, remote_id_addr_lower[NI_MAXHOST];
	static char    *remote_id_proto, remote_id_port[PMAX];
	static char     remote_filter_port[PMAX], local_filter_port[PMAX];
	static char    *remote_filter_proto, *local_filter_proto, *pfs;
	static char    *initiator, remote_filter_proto_num[3];
	static char	local_filter_proto_num[3], remote_id_proto_num[3];
	static char     phase1_group[PMAX];

	/* 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 = "";
		ah_ecn = esp_ecn = comp_ecn = "no";
		esp_encapsulation = comp_encapsulation = "";
		remote_filter_type = "";
		local_filter_type = remote_id_type = initiator = "";
		remote_filter_proto = local_filter_proto = "";
		remote_id_proto = "";

		free(remote_filter);
		remote_filter = 0;
		free(local_filter);
		local_filter = 0;
		free(remote_id);
		remote_id = 0;

		bzero(remote_ike_address, sizeof remote_ike_address);
		bzero(local_ike_address, sizeof local_ike_address);
		bzero(ah_life_kbytes, sizeof ah_life_kbytes);
		bzero(ah_life_seconds, sizeof ah_life_seconds);
		bzero(esp_life_kbytes, sizeof esp_life_kbytes);
		bzero(esp_life_seconds, sizeof esp_life_seconds);
		bzero(comp_life_kbytes, sizeof comp_life_kbytes);
		bzero(comp_life_seconds, sizeof comp_life_seconds);
		bzero(ah_key_length, sizeof ah_key_length);
		bzero(ah_key_rounds, sizeof ah_key_rounds);
		bzero(esp_key_length, sizeof esp_key_length);
		bzero(esp_key_rounds, sizeof esp_key_rounds);
		bzero(comp_dict_size, sizeof comp_dict_size);
		bzero(comp_private_alg, sizeof comp_private_alg);
		bzero(remote_filter_addr_upper,
		    sizeof remote_filter_addr_upper);
		bzero(remote_filter_addr_lower,
		    sizeof remote_filter_addr_lower);
		bzero(local_filter_addr_upper,
		    sizeof local_filter_addr_upper);
		bzero(local_filter_addr_lower,
		    sizeof local_filter_addr_lower);
		bzero(remote_id_addr_upper, sizeof remote_id_addr_upper);
		bzero(remote_id_addr_lower, sizeof remote_id_addr_lower);
		bzero(ah_group_desc, sizeof ah_group_desc);
		bzero(esp_group_desc, sizeof esp_group_desc);
		bzero(remote_id_port, sizeof remote_id_port);
		bzero(remote_filter_port, sizeof remote_filter_port);
		bzero(local_filter_port, sizeof local_filter_port);
		bzero(phase1_group, 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;
		snprintf(phase1_group, sizeof 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_SHA2_256:
					ah_auth_alg = "sha2-256";
					break;

				case IPSEC_AH_SHA2_384:
					ah_auth_alg = "sha2-384";
					break;

				case IPSEC_AH_SHA2_512:
					ah_auth_alg = "sha2-512";
					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:
				case IPSEC_ESP_AES_128_CTR:
					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)
								snprintf(ah_life_seconds, sizeof ah_life_seconds,
								    "%u", decode_16(value));
							else
								snprintf(ah_life_seconds, sizeof ah_life_seconds,
								    "%u", decode_32(value));
						} else {
							if (len == 2)
								snprintf(ah_life_kbytes, sizeof ah_life_kbytes,
								    "%u", decode_16(value));
							else
								snprintf(ah_life_kbytes, sizeof ah_life_kbytes,
								    "%u", decode_32(value));
						}

						break;

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

						break;

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

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

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

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

				case IPSEC_ATTR_ECN_TUNNEL:
					if (decode_16(value))
						switch (proto->proto) {
						case IPSEC_PROTO_IPSEC_AH:
							ah_ecn = "yes";
							break;

						case IPSEC_PROTO_IPSEC_ESP:
							esp_ecn = "yes";
							break;

						case IPSEC_PROTO_IPCOMP:
							comp_ecn = "yes";
							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 if (decode_16(value) ==
					    IPSEC_ENCAP_UDP_ENCAP_TUNNEL ||
					    decode_16(value) ==
					    IPSEC_ENCAP_UDP_ENCAP_TUNNEL_DRAFT)
						switch (proto->proto) {
						case IPSEC_PROTO_IPSEC_AH:
							ah_encapsulation = "udp-encap-tunnel";
							break;

						case IPSEC_PROTO_IPSEC_ESP:
							esp_encapsulation = "udp-encap-tunnel";
							break;

						case IPSEC_PROTO_IPCOMP:
							comp_encapsulation = "udp-encap-tunnel";
							break;
						}
					/* XXX IPSEC_ENCAP_UDP_ENCAP_TRANSPORT */
					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_HMAC_SHA2_256:
							ah_auth_alg = "hmac-sha2-256";
							break;

						case IPSEC_AUTH_HMAC_SHA2_384:
							ah_auth_alg = "hmac-sha2-384";
							break;

						case IPSEC_AUTH_HMAC_SHA2_512:
							ah_auth_alg = "hmac-sha2-512";
							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_HMAC_SHA2_256:
							esp_auth_alg = "hmac-sha2-256";
							break;

						case IPSEC_AUTH_HMAC_SHA2_384:
							esp_auth_alg = "hmac-sha2-384";
							break;

						case IPSEC_AUTH_HMAC_SHA2_512:
							esp_auth_alg = "hmac-sha2-512";
							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:
						snprintf(ah_key_length,
						    sizeof ah_key_length, "%u",
						    decode_16(value));
						break;

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

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

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

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

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

		policy_sa->transport->vtbl->get_src(policy_sa->transport,
		    &sin);
		if (sockaddr2text(sin, &addr, 1)) {
			log_error("policy_callback: sockaddr2text failed");
			goto bad;
		}
		strlcpy(local_ike_address, addr, sizeof local_ike_address);
		free(addr);

		policy_sa->transport->vtbl->get_dst(policy_sa->transport,
		    &sin);
		if (sockaddr2text(sin, &addr, 1)) {
			log_error("policy_callback: sockaddr2text failed");
			goto bad;
		}
		strlcpy(remote_ike_address, addr, sizeof remote_ike_address);
		free(addr);

		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);
			len = strlen(remote_id_addr_upper) +
			    strlen(remote_id_addr_lower) + 2;
			remote_id = calloc(len, sizeof(char));
			if (!remote_id) {
				log_error("policy_callback: "
				    "calloc (%d, %lu) failed", len,
				    (unsigned long)sizeof(char));
				goto bad;
			}
			strlcpy(remote_id, remote_id_addr_lower, len);
			strlcat(remote_id, "-", len);
			strlcat(remote_id, remote_id_addr_upper, len);
			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);
			len = strlen(remote_id_addr_upper) +
				 strlen(remote_id_addr_lower) + 2;
			remote_id = calloc(len, sizeof(char));
			if (!remote_id) {
				log_error("policy_callback: "
				    "calloc (%d, %lu) failed", len,
				    (unsigned long)sizeof(char));
				goto bad;
			}
			strlcpy(remote_id, remote_id_addr_lower, len);
			strlcat(remote_id, "-", len);
			strlcat(remote_id, remote_id_addr_upper, len);
			break;

		case IPSEC_ID_IPV6_ADDR:
			remote_id_type = "IPv6 address";
			my_inet_ntop6(id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ,
			    remote_id_addr_upper, sizeof remote_id_addr_upper);
			strlcpy(remote_id_addr_lower, remote_id_addr_upper,
			    sizeof remote_id_addr_lower);
			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_IPV6_RANGE:
			remote_id_type = "IPv6 range";

			my_inet_ntop6(id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ,
			    remote_id_addr_lower,
			    sizeof remote_id_addr_lower - 1);

			my_inet_ntop6(id + ISAKMP_ID_DATA_OFF -
			    ISAKMP_GEN_SZ + 16, remote_id_addr_upper,
			    sizeof remote_id_addr_upper - 1);

			len = strlen(remote_id_addr_upper) +
			    strlen(remote_id_addr_lower) + 2;
			remote_id = calloc(len, sizeof(char));
			if (!remote_id) {
				log_error("policy_callback: "
				    "calloc (%d, %lu) failed", len,
				    (unsigned long)sizeof(char));
				goto bad;
			}
			strlcpy(remote_id, remote_id_addr_lower, len);
			strlcat(remote_id, "-", len);
			strlcat(remote_id, remote_id_addr_upper, len);
			break;

		case IPSEC_ID_IPV6_ADDR_SUBNET:
		    {
			struct in6_addr net, mask;

			remote_id_type = "IPv6 subnet";

			bcopy(id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ, &net,
			    sizeof(net));
			bcopy(id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ + 16,
			    &mask, sizeof(mask));

			for (i = 0; i < 16; i++)
				net.s6_addr[i] &= mask.s6_addr[i];

			my_inet_ntop6((unsigned char *)&net,
			    remote_id_addr_lower,
			    sizeof remote_id_addr_lower - 1);

			for (i = 0; i < 16; i++)
				net.s6_addr[i] |= ~mask.s6_addr[i];

			my_inet_ntop6((unsigned char *)&net,
			    remote_id_addr_upper,
			    sizeof remote_id_addr_upper - 1);

			len = strlen(remote_id_addr_upper) +
			    strlen(remote_id_addr_lower) + 2;
			remote_id = calloc(len, sizeof(char));
			if (!remote_id) {
				log_error("policy_callback: "
				    "calloc (%d, %lu) failed", len,
				    (unsigned long)sizeof(char));
				goto bad;
			}
			strlcpy(remote_id, remote_id_addr_lower, len);
			strlcat(remote_id, "-", len);
			strlcat(remote_id, remote_id_addr_upper, len);
			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 (%lu, %lu) failed",
				    (unsigned long)id_sz - ISAKMP_ID_DATA_OFF +
				    ISAKMP_GEN_SZ + 1,
				    (unsigned long)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 (%lu, %lu) failed",
				    (unsigned long)id_sz - ISAKMP_ID_DATA_OFF +
				    ISAKMP_GEN_SZ + 1,
				    (unsigned long)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:
			remote_id_type = "ASN1 DN";

			remote_id = x509_DN_string(id + ISAKMP_ID_DATA_OFF -
			    ISAKMP_GEN_SZ,
			    id_sz - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ);
			if (!remote_id) {
				LOG_DBG((LOG_POLICY, 50,
				    "policy_callback: failed to decode name"));
				goto bad;
			}
			break;

		case IPSEC_ID_DER_ASN1_GN:	/* XXX */
			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 (%lu, %lu) failed",
				    2 * ((unsigned long)id_sz -
					ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ) + 1,
				    (unsigned long)sizeof(char));
				goto bad;
			}
			/* Does it contain any non-printable characters ? */
			for (i = 0;
			    i < id_sz - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ;
			    i++)
				if (!isprint(*(id + ISAKMP_ID_DATA_OFF -
				    ISAKMP_GEN_SZ + i)))
					break;
			if (i >= id_sz - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ) {
				memcpy(remote_id, id + ISAKMP_ID_DATA_OFF -
				    ISAKMP_GEN_SZ,
				    id_sz - ISAKMP_ID_DATA_OFF +
				    ISAKMP_GEN_SZ);
				break;
			}
			/* Non-printable characters, convert to hex */
			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;

		case IPPROTO_ETHERIP:
			remote_id_proto = "etherip";
			break;

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

		snprintf(remote_id_port, sizeof remote_id_port, "%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);
				len = strlen(remote_filter_addr_upper) +
				    strlen(remote_filter_addr_lower) + 2;
				remote_filter = calloc(len, sizeof(char));
				if (!remote_filter) {
					log_error("policy_callback: calloc "
					    "(%d, %lu) failed", len,
					    (unsigned long)sizeof(char));
					goto bad;
				}
				strlcpy(remote_filter,
				    remote_filter_addr_lower, len);
				strlcat(remote_filter, "-", len);
				strlcat(remote_filter,
				    remote_filter_addr_upper, len);
				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);
				len = strlen(remote_filter_addr_upper) +
				    strlen(remote_filter_addr_lower) + 2;
				remote_filter = calloc(len, sizeof(char));
				if (!remote_filter) {
					log_error("policy_callback: calloc "
					    "(%d, %lu) failed", len,
					    (unsigned long)sizeof(char));
					goto bad;
				}
				strlcpy(remote_filter,
				    remote_filter_addr_lower, len);
				strlcat(remote_filter, "-", len);
				strlcat(remote_filter,
				    remote_filter_addr_upper, len);
				break;

			case IPSEC_ID_IPV6_ADDR:
				remote_filter_type = "IPv6 address";
				my_inet_ntop6(idremote + ISAKMP_ID_DATA_OFF,
				    remote_filter_addr_upper,
				    sizeof remote_filter_addr_upper - 1);
				strlcpy(remote_filter_addr_lower,
				    remote_filter_addr_upper,
				    sizeof remote_filter_addr_lower);
				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_IPV6_RANGE:
				remote_filter_type = "IPv6 range";

				my_inet_ntop6(idremote + ISAKMP_ID_DATA_OFF,
				    remote_filter_addr_lower,
				    sizeof remote_filter_addr_lower - 1);

				my_inet_ntop6(idremote + ISAKMP_ID_DATA_OFF +
				    16, remote_filter_addr_upper,
				    sizeof remote_filter_addr_upper - 1);

				len = strlen(remote_filter_addr_upper) +
				    strlen(remote_filter_addr_lower) + 2;
				remote_filter = calloc(len, sizeof(char));
				if (!remote_filter) {
					log_error("policy_callback: calloc "
					    "(%d, %lu) failed", len,
					    (unsigned long)sizeof(char));
					goto bad;
				}
				strlcpy(remote_filter,
				    remote_filter_addr_lower, len);
				strlcat(remote_filter, "-", len);
				strlcat(remote_filter,
				    remote_filter_addr_upper, len);
				break;

			case IPSEC_ID_IPV6_ADDR_SUBNET:
				{
					struct in6_addr net, mask;

					remote_filter_type = "IPv6 subnet";

					bcopy(idremote + ISAKMP_ID_DATA_OFF,
					    &net, sizeof(net));
					bcopy(idremote + ISAKMP_ID_DATA_OFF +
					    16, &mask, sizeof(mask));

					for (i = 0; i < 16; i++)
						net.s6_addr[i] &=
						    mask.s6_addr[i];

					my_inet_ntop6((unsigned char *)&net,
					    remote_filter_addr_lower,
					sizeof remote_filter_addr_lower - 1);

					for (i = 0; i < 16; i++)
						net.s6_addr[i] |=
						    ~mask.s6_addr[i];

					my_inet_ntop6((unsigned char *)&net,
					    remote_filter_addr_upper,
					sizeof remote_filter_addr_upper - 1);

					len = strlen(remote_filter_addr_upper)
						+ strlen(remote_filter_addr_lower) + 2;
					remote_filter = calloc(len,
					    sizeof(char));
					if (!remote_filter) {
						log_error("policy_callback: "
						    "calloc (%d, %lu) failed",
						    len,
						    (unsigned long)sizeof(char));
						goto bad;
					}
					strlcpy(remote_filter,
					    remote_filter_addr_lower, len);
					strlcat(remote_filter, "-", len);
					strlcat(remote_filter,
					    remote_filter_addr_upper, len);
					break;
				}

			case IPSEC_ID_FQDN:
				remote_filter_type = "FQDN";
				remote_filter = malloc(idremotesz -
				    ISAKMP_ID_DATA_OFF + 1);
				if (!remote_filter) {
					log_error("policy_callback: "
					    "malloc (%lu) failed",
					    (unsigned long)idremotesz -
					    ISAKMP_ID_DATA_OFF + 1);
					goto bad;
				}
				memcpy(remote_filter,
				    idremote + ISAKMP_ID_DATA_OFF,
				    idremotesz - ISAKMP_ID_DATA_OFF);
				remote_filter[idremotesz - ISAKMP_ID_DATA_OFF]
				    = '\0';
				break;

			case IPSEC_ID_USER_FQDN:
				remote_filter_type = "User FQDN";
				remote_filter = malloc(idremotesz -
				    ISAKMP_ID_DATA_OFF + 1);
				if (!remote_filter) {
					log_error("policy_callback: "
					    "malloc (%lu) failed",
					    (unsigned long)idremotesz -
					    ISAKMP_ID_DATA_OFF + 1);
					goto bad;
				}
				memcpy(remote_filter,
				    idremote + ISAKMP_ID_DATA_OFF,
				    idremotesz - ISAKMP_ID_DATA_OFF);
				remote_filter[idremotesz - ISAKMP_ID_DATA_OFF]
				    = '\0';
				break;

			case IPSEC_ID_DER_ASN1_DN:
				remote_filter_type = "ASN1 DN";

				remote_filter = x509_DN_string(idremote +
				    ISAKMP_ID_DATA_OFF,
				    idremotesz - ISAKMP_ID_DATA_OFF);
				if (!remote_filter) {
					LOG_DBG((LOG_POLICY, 50,
					    "policy_callback: "
					    "failed to decode name"));
					goto bad;
				}
				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 (%lu, %lu) failed",
					    2 * ((unsigned long)idremotesz -
						ISAKMP_ID_DATA_OFF) + 1,
					    (unsigned long)sizeof(char));
					goto bad;
				}
				/*
				 * Does it contain any non-printable
				 * characters ?
				 */
				for (i = 0;
				    i < idremotesz - ISAKMP_ID_DATA_OFF; i++)
					if (!isprint(*(idremote +
					    ISAKMP_ID_DATA_OFF + i)))
						break;
				if (i >= idremotesz - ISAKMP_ID_DATA_OFF) {
					memcpy(remote_filter,
					    idremote + ISAKMP_ID_DATA_OFF,
					    idremotesz - ISAKMP_ID_DATA_OFF);
					break;
				}
				/* Non-printable characters, convert to hex */
				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;

			case IPPROTO_ETHERIP:
				remote_filter_proto = "etherip";
				break;

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

			snprintf(remote_filter_port, sizeof remote_filter_port,
			    "%u", decode_16(idremote + ISAKMP_GEN_SZ + 2));
		} else {
			policy_sa->transport->vtbl->get_dst(policy_sa->transport, &sin);
			switch (sin->sa_family) {
			case AF_INET:
				remote_filter_type = "IPv4 address";
				break;
			case AF_INET6:
				remote_filter_type = "IPv6 address";
				break;
			default:
				log_print("policy_callback: "
				    "unsupported protocol family %d",
				    sin->sa_family);
				goto bad;
			}
			if (sockaddr2text(sin, &addr, 1)) {
				log_error("policy_callback: "
				    "sockaddr2text failed");
				goto bad;
			}
			memcpy(remote_filter_addr_upper, addr,
			    sizeof remote_filter_addr_upper);
			memcpy(remote_filter_addr_lower, addr,
			    sizeof remote_filter_addr_lower);
			free(addr);
			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);
				len = strlen(local_filter_addr_upper)
					+ strlen(local_filter_addr_lower) + 2;
				local_filter = calloc(len, sizeof(char));
				if (!local_filter) {
					log_error("policy_callback: "
					    "calloc (%d, %lu) failed", len,
					    (unsigned long)sizeof(char));
					goto bad;
				}
				strlcpy(local_filter, local_filter_addr_lower,
				    len);
				strlcat(local_filter, "-", len);
				strlcat(local_filter, local_filter_addr_upper,
				    len);
				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);
				len = strlen(local_filter_addr_upper) +
				    strlen(local_filter_addr_lower) + 2;
				local_filter = calloc(len, sizeof(char));
				if (!local_filter) {
					log_error("policy_callback: "
					    "calloc (%d, %lu) failed", len,
					    (unsigned long)sizeof(char));
					goto bad;
				}
				strlcpy(local_filter, local_filter_addr_lower,
				    len);
				strlcat(local_filter, "-", len);
				strlcat(local_filter, local_filter_addr_upper,
				    len);
				break;

			case IPSEC_ID_IPV6_ADDR:
				local_filter_type = "IPv6 address";
				my_inet_ntop6(idlocal + ISAKMP_ID_DATA_OFF,
				    local_filter_addr_upper,
				    sizeof local_filter_addr_upper - 1);
				strlcpy(local_filter_addr_lower,
				    local_filter_addr_upper,
				    sizeof local_filter_addr_lower);
				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_IPV6_RANGE:
				local_filter_type = "IPv6 range";

				my_inet_ntop6(idlocal + ISAKMP_ID_DATA_OFF,
				    local_filter_addr_lower,
				    sizeof local_filter_addr_lower - 1);

				my_inet_ntop6(idlocal + ISAKMP_ID_DATA_OFF +
				    16, local_filter_addr_upper,
				    sizeof local_filter_addr_upper - 1);

				len = strlen(local_filter_addr_upper)
					+ strlen(local_filter_addr_lower) + 2;
				local_filter = calloc(len, sizeof(char));
				if (!local_filter) {
					log_error("policy_callback: "
					    "calloc (%d, %lu) failed", len,
					    (unsigned long)sizeof(char));
					goto bad;
				}
				strlcpy(local_filter, local_filter_addr_lower,
				    len);
				strlcat(local_filter, "-", len);
				strlcat(local_filter, local_filter_addr_upper,
				    len);
				break;

			case IPSEC_ID_IPV6_ADDR_SUBNET:
				{
					struct in6_addr net, mask;

					local_filter_type = "IPv6 subnet";

					bcopy(idlocal + ISAKMP_ID_DATA_OFF,
					    &net, sizeof(net));
					bcopy(idlocal + ISAKMP_ID_DATA_OFF +
					    16, &mask, sizeof(mask));

					for (i = 0; i < 16; i++)
						net.s6_addr[i] &=
						    mask.s6_addr[i];

					my_inet_ntop6((unsigned char *)&net,
					    local_filter_addr_lower,
					sizeof local_filter_addr_lower - 1);

					for (i = 0; i < 16; i++)
						net.s6_addr[i] |=
						    ~mask.s6_addr[i];

					my_inet_ntop6((unsigned char *)&net,
					    local_filter_addr_upper,
					    sizeof local_filter_addr_upper -
					    1);

					len = strlen(local_filter_addr_upper)
					    + strlen(local_filter_addr_lower)
					    + 2;
					local_filter = calloc(len,
					    sizeof(char));
					if (!local_filter) {
						log_error("policy_callback: "
						    "calloc (%d, %lu) failed",
						    len,
						    (unsigned long)sizeof(char));
						goto bad;
					}
					strlcpy(local_filter,
					    local_filter_addr_lower, len);
					strlcat(local_filter, "-", len);
					strlcat(local_filter,
					    local_filter_addr_upper, len);
					break;
				}

			case IPSEC_ID_FQDN:
				local_filter_type = "FQDN";
				local_filter = malloc(idlocalsz -
				    ISAKMP_ID_DATA_OFF + 1);
				if (!local_filter) {
					log_error("policy_callback: "
					    "malloc (%lu) failed",
					    (unsigned long)idlocalsz -
					    ISAKMP_ID_DATA_OFF + 1);
					goto bad;
				}
				memcpy(local_filter,
				    idlocal + ISAKMP_ID_DATA_OFF,
				    idlocalsz - ISAKMP_ID_DATA_OFF);
				local_filter[idlocalsz - ISAKMP_ID_DATA_OFF] = '\0';
				break;

			case IPSEC_ID_USER_FQDN:
				local_filter_type = "User FQDN";
				local_filter = malloc(idlocalsz -
				    ISAKMP_ID_DATA_OFF + 1);
				if (!local_filter) {
					log_error("policy_callback: "
					    "malloc (%lu) failed",
					    (unsigned long)idlocalsz -
					    ISAKMP_ID_DATA_OFF + 1);
					goto bad;
				}
				memcpy(local_filter,
				    idlocal + ISAKMP_ID_DATA_OFF,
				    idlocalsz - ISAKMP_ID_DATA_OFF);
				local_filter[idlocalsz - ISAKMP_ID_DATA_OFF] = '\0';
				break;

			case IPSEC_ID_DER_ASN1_DN:
				local_filter_type = "ASN1 DN";

				local_filter = x509_DN_string(idlocal +
				    ISAKMP_ID_DATA_OFF,
				    idlocalsz - ISAKMP_ID_DATA_OFF);
				if (!local_filter) {
					LOG_DBG((LOG_POLICY, 50,
					    "policy_callback: failed to decode"
					    " name"));
					goto bad;
				}
				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 (%lu, %lu) failed",
					    2 * ((unsigned long)idlocalsz -
					    ISAKMP_ID_DATA_OFF) + 1,
					    (unsigned long)sizeof(char));
					goto bad;
				}
				/*
				 * Does it contain any non-printable
				 * characters ?
				 */
				for (i = 0;
				    i < idlocalsz - ISAKMP_ID_DATA_OFF; i++)
					if (!isprint(*(idlocal +
					    ISAKMP_ID_DATA_OFF + i)))
						break;
				if (i >= idlocalsz - ISAKMP_ID_DATA_OFF) {
					memcpy(local_filter, idlocal +
					    ISAKMP_ID_DATA_OFF,
					    idlocalsz - ISAKMP_ID_DATA_OFF);
					break;
				}
				/* Non-printable characters, convert to hex */
				for (i = 0;
				    i < idlocalsz - 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;

			case IPPROTO_ETHERIP:
				local_filter_proto = "etherip";
				break;

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

			snprintf(local_filter_port, sizeof local_filter_port,
			    "%u", decode_16(idlocal + ISAKMP_GEN_SZ + 2));
		} else {
			policy_sa->transport->vtbl->get_src(policy_sa->transport,
			    (struct sockaddr **)&sin);
			switch (sin->sa_family) {
			case AF_INET:
				local_filter_type = "IPv4 address";
				break;
			case AF_INET6:
				local_filter_type = "IPv6 address";
				break;
			default:
				log_print("policy_callback: "
				    "unsupported protocol family %d",
				    sin->sa_family);
				goto bad;
			}

			if (sockaddr2text(sin, &addr, 1)) {
				log_error("policy_callback: "
				    "sockaddr2text failed");
				goto bad;
			}
			memcpy(local_filter_addr_upper, addr,
			    sizeof local_filter_addr_upper);
			memcpy(local_filter_addr_lower, addr,
			    sizeof local_filter_addr_lower);
			free(addr);
			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, "ah_ecn == %s", ah_ecn));
		LOG_DBG((LOG_POLICY, 80, "esp_ecn == %s", esp_ecn));
		LOG_DBG((LOG_POLICY, 80, "comp_ecn == %s", comp_ecn));
		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, "%Y%m%d%H%M%S", gmtime(&tt));
		return mytimeofday;
	}
	if (strcmp(name, "LocalTimeOfDay") == 0) {
		tt = time((time_t)NULL);
		strftime(mytimeofday, 14, "%Y%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;

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

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

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

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

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

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

	return "";

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

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

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

	/* Do we want to use the policy modules?  */
	if (ignore_policy ||
	    strncmp("yes", conf_get_str("General", "Use-Keynote"), 3))
		return;

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

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

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

	/* Allocate memory to keep policies.  */
	ptr = calloc(sz + 1, sizeof(char));
	if (!ptr)
		log_fatal("policy_init: calloc (%lu, %lu) failed",
		    (unsigned long)sz + 1, (unsigned long)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, %lu) failed", fd,
			    ptr + len, (unsigned long)(sz - len));
	}

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

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

	/* Begone!  */
	free(ptr);

	if (asserts == (char **)NULL)
		log_print("policy_init: all policies flushed");

	/* Cleanup */
	if (policy_asserts) {
		for (fd = 0; fd < policy_asserts_num; fd++)
			if (policy_asserts)
				free(policy_asserts[fd]);

		free(policy_asserts);
	}
	policy_asserts = asserts;
	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 = malloc(len + 1);

	if (foo == NULL)
		return NULL;

	memcpy(foo, data, len);
	foo[len] = '\0';
	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 = kn_read_asserts((char *)scert, strlen((char *)scert), &num);
	if (foo == NULL)
		return 0;

	for (i = 0; i < num; i++) {
		if (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 = kn_read_asserts((char *)scert, strlen((char *)scert), &num);
	if (foo == NULL)
		return 0;

	while (num--)
		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, %lu) failed",
		    len + 1, (unsigned long)sizeof(char));
		return 0;
	}
	memcpy(dat, data, len);

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

	free(dat);

	return err;
}

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

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, *addr_str;
	struct stat     sb;
	size_t          size;
	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:
	case IPSEC_ID_IPV6_ADDR:
		util_ntoa(&addr_str, idtype == IPSEC_ID_IPV4_ADDR ?
		    AF_INET : AF_INET6, id);
		if (addr_str == 0)
			return 0;

		if (asprintf(&file, "%s/%s/%s", dirname,
		    addr_str, CREDENTIAL_FILE) == -1) {
			log_error("keynote_cert_obtain: failed to allocate "
			    "%lu bytes", (unsigned long)len +
			    strlen(addr_str));
			free(addr_str);
			return 0;
		}
		free(addr_str);
		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 %lu bytes",
			    (unsigned long)len + id_len);
			return 0;
		}
		snprintf(file, len + id_len, "%s/", dirname);
		memcpy(file + strlen(dirname) + 1, id, id_len);
		snprintf(file + strlen(dirname) + 1 + id_len,
		    len - strlen(dirname) - 1, "/%s", CREDENTIAL_FILE);
		break;

	default:
		return 0;
	}

	fd = monitor_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 (fstat(fd, &sb) < 0) {
		LOG_DBG((LOG_POLICY, 30, "keynote_cert_obtain: "
		    "failed to stat \"%s\"", file));
		free(file);
		close(fd);
		return 0;
	}
	size = (size_t)sb.st_size;

	*cert = calloc(size + 1, sizeof(char));
	if (*cert == NULL) {
		log_error("keynote_cert_obtain: failed to allocate %lu bytes",
		    (unsigned long)size);
		free(file);
		close(fd);
		return 0;
	}

	if (read(fd, *cert, size) != (int)size) {
		LOG_DBG((LOG_POLICY, 30, "keynote_cert_obtain: "
		    "failed to read %lu bytes from \"%s\"",
		    (unsigned long)size, file));
		free(cert);
		cert = NULL;
		free(file);
		close(fd);
		return 0;
	}
	close(fd);
	free(file);
	*certlen = 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, kid, num;
	char          **foo;

	foo = kn_read_asserts((char *)scert, strlen((char *)scert), &num);
	if (foo == NULL || num == 0) {
		log_print("keynote_cert_get_key: "
		    "failed to decompose credentials");
		return 0;
	}
	kid = kn_init();
	if (kid == -1) {
		log_print("keynote_cert_get_key: "
		    "failed to initialize new policy session");
		while (num--)
			free(foo[num]);
		free(foo);
		return 0;
	}
	sid = kn_add_assertion(kid, foo[num - 1], strlen(foo[num - 1]), 0);
	while (num--)
		free(foo[num]);
	free(foo);

	if (sid == -1) {
		log_print("keynote_cert_get_key: failed to add assertion");
		kn_close(kid);
		return 0;
	}
	*(RSA **)keyp = NULL;

	kl = kn_get_licensees(kid, sid);
	while (kl) {
		if (kl->key_alg == KEYNOTE_ALGORITHM_RSA ||
		    kl->key_alg == KEYNOTE_ALGORITHM_X509) {
			*(RSA **)keyp = RSAPublicKey_dup(kl->key_key);
			break;
		}
		kl = kl->key_next;
	}

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

void *
keynote_cert_dup(void *cert)
{
	return strdup((char *)cert);
}

void
keynote_serialize(void *cert, u_int8_t **data, u_int32_t *datalen)
{
	*datalen = strlen((char *)cert) + 1;
	*data = (u_int8_t *)strdup(cert);	/* i.e an extra character at
						 * the end... */
	if (*data == NULL)
		log_error("keynote_serialize: malloc (%d) failed", *datalen);
}

/* From cert to printable */
char *
keynote_printable(void *cert)
{
	return strdup((char *)cert);
}

/* From printable to cert */
void *
keynote_from_printable(char *cert)
{
	return strdup(cert);
}

/* Number of CAs we trust (currently this is x509 only) */
int
keynote_ca_count(void)
{
	return 0;
}