/*
 * The IKE Scanner (ike-scan) is Copyright (C) 2003-2005 Roy Hills,
 * NTA Monitor Ltd.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * If this license is unacceptable to you, I may be willing to negotiate
 * alternative licenses (contact ike-scan@nta-monitor.com).
 *
 * You are encouraged to send comments, improvements or suggestions to
 * me at ike-scan@nta-monitor.com.
 *
 * $Id: isakmp.c,v 1.1.1.1 2005/01/13 18:45:15 mcr Exp $
 *
 * Author: Roy Hills
 * Date: 7 November 2003
 *
 * Functions to construct ISAKMP headers and payloads.
 *
 */

#include "ike-scan.h"

static char rcsid[] = "$Id: isakmp.c,v 1.1.1.1 2005/01/13 18:45:15 mcr Exp $";  /* RCS ID for ident(1) */

extern int experimental_flag;
extern struct psk_crack psk_values;

/*
 *	make_isakmp_hdr -- Construct an ISAKMP Header
 *
 *	Inputs:
 *
 *	xchg	Exchange Type (e.g. ISAKMP_XCHG_IDPROT for main mode)
 *	next	Next Payload Type
 *	length	ISAKMP Message total length
 *
 *	Returns:
 *
 *	Pointer to created ISAKMP Header.
 *
 *	This constructs an ISAKMP header.  It fills in the static values.
 *	The initator cookie should be changed to a unique per-host value
 *	before the packet is sent.
 */
struct isakmp_hdr*make_isakmp_hdr(unsigned xchg, unsigned next,
				  unsigned length)
{
	struct isakmp_hdr* hdr;

	hdr = Malloc(sizeof(struct isakmp_hdr));
	memset(hdr, '\0', sizeof(struct isakmp_hdr));

	hdr->isa_icookie[0] = 0xdeadbeef;       /* Initiator cookie */
	hdr->isa_icookie[1] = 0xdeadbeef;
	hdr->isa_rcookie[0] = 0;                /* Set responder cookie to 0 */
	hdr->isa_rcookie[1] = 0;
	hdr->isa_np = next;                     /* Next Payload Type */
	hdr->isa_version = 0x10;                /* v1.0 */
	hdr->isa_xchg = xchg;                   /* Exchange type */
	hdr->isa_flags = 0;                     /* No flags */
	hdr->isa_msgid = 0;                     /* MBZ for phase-1 */
	hdr->isa_length = htonl(length);        /* Total ISAKMP message length */

	return hdr;
}

/*
 *	make_sa_hdr -- Construct an SA Header
 *
 *	Inputs:
 *
 *	next    Next Payload Type
 *	length	SA payload length
 *
 *	Returns:
 *
 *	Pointer to SA Header.
 *
 *	This constructs an SA header.  It fills in the static values.
 */
struct isakmp_sa*make_sa_hdr(unsigned next, unsigned length)
{
	struct isakmp_sa* hdr;

	hdr = Malloc(sizeof(struct isakmp_sa));
	memset(hdr, '\0', sizeof(struct isakmp_sa));

	hdr->isasa_np = next;                                   /* Next Payload Type */
	hdr->isasa_length = htons(length);                      /* SA Payload length */
	hdr->isasa_doi = htonl(ISAKMP_DOI_IPSEC);               /* IPsec DOI */
	hdr->isasa_situation = htonl(SIT_IDENTITY_ONLY);        /* Exchange type */

	return hdr;
}

/*
 *	make_prop -- Construct a proposal payload
 *
 *	Inputs:
 *
 *	length	Proposal payload length
 *	notrans	Number of transforms in this proposal
 *
 *	Returns:
 *
 *	Pointer to proposal payload.
 *
 *	This constructs a proposal payload.  It fills in the static values.
 *	We assume only one proposal will be created.  I think that ISAKMP SAs
 *	are only allowed to have one proposal anyway.
 */
struct isakmp_proposal*make_prop(unsigned length, unsigned notrans)
{
	struct isakmp_proposal* hdr;

	hdr = Malloc(sizeof(struct isakmp_proposal));
	memset(hdr, '\0', sizeof(struct isakmp_proposal));

	hdr->isap_np = 0;                       /* No more proposals */
	hdr->isap_length = htons(length);       /* Proposal payload length */
	hdr->isap_proposal = 1;                 /* Proposal #1 */
	hdr->isap_protoid = PROTO_ISAKMP;
	hdr->isap_spisize = 0;                  /* No SPI */
	hdr->isap_notrans = notrans;            /* Number of transforms */

	return hdr;
}

/*
 *	make_trans -- Construct a single transform payload
 *
 *	Inputs:
 *
 *	length	(output) length of entire transform payload.
 *	next    Next Payload Type (3 = More transforms; 0=No more transforms)
 *	number	Transform number
 *	cipher	The encryption algorithm
 *	keylen	Key length for variable length keys (0=fixed key length)
 *	hash	Hash algorithm
 *	auth	Authentication method
 *	group	DH Group number
 *	lifetime	Lifetime in seconds (0=no lifetime)
 *	lifesize	Life in kilobytes (0=no life)
 *
 *	Returns:
 *
 *	Pointer to transform payload.
 *
 *	This constructs a single transform payload.
 *	Most of the values are defined in RFC 2409 Appendix A.
 */
unsigned char*make_trans(size_t *length, unsigned next, unsigned number,
			 unsigned cipher,
			 unsigned keylen, unsigned hash, unsigned auth,
			 unsigned group,
			 unsigned lifetime, unsigned lifesize, int gss_id_flag,
			 unsigned char *gss_data, size_t gss_data_len)
{

	struct isakmp_transform* hdr;   /* Transform header */
	unsigned char *payload;
	unsigned char *attr;
	unsigned char *cp;
	size_t attr_len;                /* Attribute Length */
	size_t len;                     /* Payload Length */

/* Allocate and initialise the transform header */

	hdr = Malloc(sizeof(struct isakmp_transform));
	memset(hdr, '\0', sizeof(struct isakmp_transform));

	hdr->isat_np = next;            /* Next payload type */
	hdr->isat_transnum = number;    /* Transform Number */
	hdr->isat_transid = KEY_IKE;

/* Allocate and initialise the mandatory attributes */

	add_attr(0, NULL, 'B', OAKLEY_ENCRYPTION_ALGORITHM, 0, cipher, NULL);
	add_attr(0, NULL, 'B', OAKLEY_HASH_ALGORITHM, 0, hash, NULL);
	add_attr(0, NULL, 'B', OAKLEY_AUTHENTICATION_METHOD, 0, auth, NULL);
	add_attr(0, NULL, 'B', OAKLEY_GROUP_DESCRIPTION, 0, group, NULL);

/* Allocate and initialise the optional attributes */

	if (keylen)
		add_attr(0, NULL, 'B', OAKLEY_KEY_LENGTH, 0, keylen, NULL);

	if (lifetime) {
		uint32_t lifetime_n = htonl(lifetime);

		add_attr(0, NULL, 'B', OAKLEY_LIFE_TYPE, 0,
			 SA_LIFE_TYPE_SECONDS, NULL);
		add_attr(0, NULL, 'V', OAKLEY_LIFE_DURATION, 4, 0,
			 &lifetime_n);
	}

	if (lifesize) {
		uint32_t lifesize_n = htonl(lifesize);

		add_attr(0, NULL, 'B', OAKLEY_LIFE_TYPE, 0,
			 SA_LIFE_TYPE_KBYTES, NULL);
		add_attr(0, NULL, 'V', OAKLEY_LIFE_DURATION, 4, 0,
			 &lifesize_n);
	}

	if (gss_id_flag)
		add_attr(0, NULL, 'V', OAKLEY_GSS_ID, gss_data_len, 0,
			 gss_data);

/* Finalise attributes and fill in length value */

	attr = add_attr(1, &attr_len, '\0', 0, 0, 0, NULL);
	len = attr_len + sizeof(struct isakmp_transform);
	hdr->isat_length = htons(len);  /* Transform length */
	*length = len;

/* Allocate memory for payload and copy structures to payload */

	payload = Malloc(len);

	cp = payload;
	memcpy(cp, hdr, sizeof(struct isakmp_transform));
	free(hdr);
	cp += sizeof(struct isakmp_transform);
	memcpy(cp, attr, attr_len);
	free(attr);

	return payload;
}

/*
 *	add_trans -- Add a transform payload onto the set of transforms.
 *
 *	Inputs:
 *
 *	finished	0 if adding a new transform; 1 if finalising.
 *	length	(output) length of entire transform payload.
 *	cipher	The encryption algorithm
 *	keylen	Key length for variable length keys (0=fixed key length)
 *	hash	Hash algorithm
 *	auth	Authentication method
 *	group	DH Group number
 *	lifetime	Lifetime in seconds (0=no lifetime)
 *
 *	Returns:
 *
 *	Pointer to new set of transform payloads.
 *
 *	This function can either be called with finished = 0, in which case
 *	cipher, keylen, hash, auth, group and lifetime must be specified, and
 *	the function will return NULL, OR it can be called with finished = 1
 *	in which case cipher, keylen, hash, auth, group and lifetime are
 *	ignored and the function will return a pointer to the finished
 *	payload and will set *length to the length of this payload.
 */
unsigned char*add_trans(int finished, size_t *length,
			unsigned cipher, unsigned keylen, unsigned hash,
			unsigned auth,
			unsigned group, unsigned lifetime, unsigned lifesize,
			int gss_id_flag, unsigned char *gss_data,
			size_t gss_data_len)
{

	static int first_transform = 1;
	static unsigned char *trans_start = NULL;       /* Start of set of transforms */
	static size_t cur_offset;                       /* Start of current transform */
	static size_t end_offset;                       /* End of transforms */
	static unsigned trans_no = 1;
	unsigned char *trans;                           /* Transform payload */
	size_t len;                                     /* Transform length */

/*
 * Construct a transform if we are not finalising.
 * Set next to 3 (more transforms), and increment trans_no for next time round.
 */
	if (!finished) {
		trans = make_trans(&len, 3, trans_no, cipher, keylen, hash,
				   auth,
				   group, lifetime, lifesize, gss_id_flag,
				   gss_data,
				   gss_data_len);
		trans_no++;
		if (first_transform) {
			cur_offset = 0;
			end_offset = len;
			trans_start = Malloc(end_offset);
			memcpy(trans_start, trans, len);
			first_transform = 0;
		} else {
			cur_offset = end_offset;
			end_offset += len;
			trans_start = Realloc(trans_start, end_offset);
			memcpy(trans_start + cur_offset, trans, len);
		}
		return NULL;
	} else {
		struct isakmp_transform* hdr =
			(struct isakmp_transform*) (trans_start + cur_offset); /* Overlay */

		first_transform = 1;
		hdr->isat_np = 0; /* No more transforms */
		*length = end_offset;
		return trans_start;
	}
}

/*
 *	make_attr -- Construct a transform attribute
 *
 *	Inputs:
 *
 *	outlen	(output) Total length of transform attribute.
 *	type	Attribute Type.  'B' = basic, 'V' = variable.
 *	class	Attribute Class
 *	length	Attribute data length for variable type (ignored for basic).
 *	b_value	Basic Attribute Value
 *	v_value	Pointer to Variable Attribute Value
 *
 *	Returns:
 *
 *	Pointer to transform attribute.
 *
 *	For variable attribute types, the data must be in network byte
 *	order, and its length should be a multiple of 4 bytes to avoid
 *	allignment issues.
 *
 *	If type is "B", then length and v_value are ignored.  If type is "V",
 *	then b_value is ignored.
 */
unsigned char *make_attr(size_t *outlen, int type, unsigned class,
			 size_t length,
			 unsigned b_value, void *v_value)
{
	struct isakmp_attribute *hdr;
	unsigned char *cp;
	size_t total_len;

	total_len = sizeof(struct isakmp_attribute);
	if (type == 'V')
		total_len += length;

	cp = Malloc(total_len);
	hdr = (struct isakmp_attribute *) cp;

	if (type == 'B') { /* Basic Attribute */
		hdr->isaat_af_type = htons(class | 0x8000);
		hdr->isaat_lv = htons(b_value);
	} else {        /* Variable Attribute */
		hdr->isaat_af_type = htons(class);
		hdr->isaat_lv = htons(length);
		memcpy(cp + sizeof(struct isakmp_attribute), v_value, length);
	}

	*outlen = total_len;
	return cp;
}

/*
 *	add_attr -- Add a new attribute onto the list of attributes
 *
 *	Inputs:
 *
 *	finished	0 if adding a new attribute; 1 if finalising.
 *	outlen	(output) Total length of attribute list.
 *	type	Attribute Type.  'B' = basic, 'V' = variable.
 *	class	Attribute Class
 *	length	Attribute data length for variable type (ignored for basic).
 *	b_value	Basic Attribute Value
 *	v_value	Pointer to Variable Attribute Value
 *
 *	Returns:
 *
 *	Pointer to attribute list
 *
 *	This function can either be called with finished = 0, in which case
 *	type, class, length and either b_value or v_value must be specified,
 *	and the function will return NULL; or it can be called with
 *	finished = 1 in which case type, class, length,  b_value and v_value
 *	are ignored and the function will return a pointer to the finished
 *	attribute list and will set *outlen to the length of the attribute
 *	list.
 */
unsigned char *add_attr(int finished, size_t *outlen, int type, unsigned class,
			size_t length,
			unsigned b_value, void *v_value)
{

	static int first_attr = 1;
	unsigned char *attr;
	static unsigned char *attr_start = NULL;        /* Start of attr list */
	static size_t cur_offset;                       /* Start of current attr */
	static size_t end_offset;                       /* End of attr list */
	size_t len;                                     /* Attr length */

/*
 *	Construct a new attribute if we are not fianlising.
 */
	if (!finished) {
		attr = make_attr(&len, type, class, length, b_value, v_value);
		if (first_attr) {
			cur_offset = 0;
			end_offset = len;
			attr_start = Malloc(end_offset);
			memcpy(attr_start, attr, len);
			first_attr = 0;
		} else {
			cur_offset = end_offset;
			end_offset += len;
			attr_start = Realloc(attr_start, end_offset);
			memcpy(attr_start + cur_offset, attr, len);
		}
		return NULL;
	} else {
		first_attr = 1;
		*outlen = end_offset;
		return attr_start;
	}
}

/*
 *	make_vid -- Construct a vendor id payload
 *
 *	Inputs:
 *
 *	length	(output) length of Vendor ID payload.
 *	next		Next Payload Type
 *	vid_data	Vendor ID data
 *	vid_data_len	Vendor ID data length
 *
 *	Returns:
 *
 *	Pointer to vendor id payload.
 *
 *	This constructs a vendor id payload.  It fills in the static values.
 *	The next pointer value must be filled in later.
 */
unsigned char*make_vid(size_t *length, unsigned next, unsigned char *vid_data,
		       size_t vid_data_len)
{
	unsigned char *payload;
	struct isakmp_vid* hdr;

	payload = Malloc(sizeof(struct isakmp_vid) + vid_data_len);
	hdr = (struct isakmp_vid*) payload; /* Overlay vid struct on payload */
	memset(hdr, '\0', sizeof(struct isakmp_vid));

	hdr->isavid_np = next;          /* Next payload type */
	hdr->isavid_length = htons(sizeof(struct isakmp_vid) + vid_data_len);

	memcpy(payload + sizeof(struct isakmp_vid), vid_data, vid_data_len);
	*length = sizeof(struct isakmp_vid) + vid_data_len;

	return payload;
}

/*
 *	add_vid -- Add a vendor ID payload to the set of VIDs.
 *
 *	Inputs:
 *
 *      finished        0 if adding a new VIDs; 1 if finalising.
 *      length  (output) length of entire VID payload set.
 *      vid_data        Vendor ID data
 *      vid_data_len    Vendor ID data length
 *
 *	Returns:
 *
 *	Pointer to the VID payload.
 *
 *	This function can either be called with finished = 0, in which case
 *	vid_data and vid_data_len must be specified, and
 *	the function will return NULL, OR it can be called with finished = 1
 *	in which case vid_data and vid_data_len are
 *	ignored and the function will return a pointer to the finished
 *	payload and will set *length to the length of this payload.
 */
unsigned char*add_vid(int finished, size_t *length, unsigned char *vid_data,
		      size_t vid_data_len)
{
	static int first_vid = 1;
	static unsigned char *vid_start = NULL; /* Start of set of VIDs */
	static size_t cur_offset;               /* Start of current VID */
	static size_t end_offset;               /* End of VIDs */
	unsigned char *vid;                     /* VID payload */
	size_t len;                             /* VID length */

/*
 * Construct a VID if we are not finalising.
 */
	if (!finished) {
		vid = make_vid(&len, ISAKMP_NEXT_VID, vid_data, vid_data_len);
		if (first_vid) {
			cur_offset = 0;
			end_offset = len;
			vid_start = Malloc(end_offset);
			memcpy(vid_start, vid, len);
			first_vid = 0;
		} else {
			cur_offset = end_offset;
			end_offset += len;
			vid_start = Realloc(vid_start, end_offset);
			memcpy(vid_start + cur_offset, vid, len);
		}
		return NULL;
	} else {
		struct isakmp_vid* hdr =
			(struct isakmp_vid*) (vid_start + cur_offset);  /* Overlay */

		hdr->isavid_np = ISAKMP_NEXT_NONE;                      /* No more payloads */
		*length = end_offset;
		return vid_start;
	}
}

/*
 *	make_ke	-- Make a Key Exchange payload
 *
 *	Inputs:
 *
 *      length		(output) length of key exchange payload.
 *      next		Next Payload Type
 *      kx_data_len	Key exchange data length
 *
 *	Returns:
 *
 *	Pointer to key exchange payload.
 *
 *	A real implementation would fill in the key exchange payload with the
 *	Diffie Hellman public value.  However, we just use random data.
 */
unsigned char*make_ke(size_t *length, unsigned next, size_t kx_data_len)
{
	unsigned char *payload;
	struct isakmp_kx* hdr;
	unsigned char *kx_data;
	unsigned i;

	if (kx_data_len % 4)
		err_msg("Key exchange data length %d is not a multiple of 4",
			kx_data_len);

	payload = Malloc(sizeof(struct isakmp_kx) + kx_data_len);
	hdr = (struct isakmp_kx*) payload; /* Overlay kx struct on payload */
	memset(hdr, '\0', sizeof(struct isakmp_kx));

	kx_data = payload + sizeof(struct isakmp_kx);
	for (i = 0; i < kx_data_len; i++)
		*(kx_data++) = (unsigned char) (rand() & 0xff);

	hdr->isakx_np = next;           /* Next payload type */
	hdr->isakx_length = htons(sizeof(struct isakmp_kx) + kx_data_len);

	*length = sizeof(struct isakmp_kx) + kx_data_len;

	return payload;
}

/*
 *	make_nonce	-- Make a Nonce payload
 *
 *	Inputs:
 *
 *	length		(output) length of nonce payload.
 *      next		Next Payload Type
 *	nonce_len	Length of nonce data.
 *
 *	Returns:
 *
 *	Pointer to nonce payload.
 *
 *	For a real implementation, the nonce should use strong random numbers.
 *	However, we just use rand() because we don't care about the quality of
 *	the random numbers for this tool.
 *
 *	RFC 2409 states that: "The length of nonce payload MUST be between 8
 *	and 256 bytes inclusive".  However, this function doesn't enforce the
 *	restriction.
 */
unsigned char*make_nonce(size_t *length, unsigned next, size_t nonce_len)
{
	unsigned char *payload;
	struct isakmp_nonce* hdr;
	unsigned char *cp;
	unsigned i;

	payload = Malloc(sizeof(struct isakmp_nonce) + nonce_len);
	hdr = (struct isakmp_nonce*) payload; /* Overlay nonce struct on payload */
	memset(hdr, '\0', sizeof(struct isakmp_nonce));

	hdr->isanonce_np = next;        /* Next payload type */
	hdr->isanonce_length = htons(sizeof(struct isakmp_nonce) + nonce_len);

	cp = payload + sizeof(struct isakmp_vid);
	for (i = 0; i < nonce_len; i++)
		*(cp++) = (unsigned char) (rand() & 0xff);

	*length = sizeof(struct isakmp_nonce) + nonce_len;
	return payload;
}

/*
 *	make_id	-- Make an Identification payload
 *
 *	Inputs:
 *
 *      length		(output) length of ID payload.
 *      next		Next Payload Type
 *	idtype		Identification Type
 *      id_data		ID data
 *      id_data_len	ID data length
 *
 */
unsigned char*make_id(size_t *length, unsigned next, unsigned idtype,
		      unsigned char *id_data,
		      size_t id_data_len)
{
	unsigned char *payload;
	struct isakmp_id* hdr;

	payload = Malloc(sizeof(struct isakmp_id) + id_data_len);
	hdr = (struct isakmp_id*) payload; /* Overlay ID struct on payload */
	memset(hdr, '\0', sizeof(struct isakmp_id));

	hdr->isaid_np = next;           /* Next payload type */
	hdr->isaid_length = htons(sizeof(struct isakmp_id) + id_data_len);
	hdr->isaid_idtype = idtype;
/*
 *	RFC 2407 4.6.2: "During Phase I negotiations, the ID port and protocol
 *	fields MUST be set to zero or to UDP port 500"
 */
	hdr->isaid_doi_specific_a = 17;         /* Protocol: UDP */
	hdr->isaid_doi_specific_b = htons(500); /* Port: 500 */

	memcpy(payload + sizeof(struct isakmp_id), id_data, id_data_len);
	*length = sizeof(struct isakmp_id) + id_data_len;

	return payload;
}

/*
 *      make_udphdr -- Construct a UDP header for encapsulated IKE
 *
 *      Inputs:
 *
 *      length  (output) length of UDP header
 *      sport           UDP source port
 *      dport           UDP destination port
 *      udplen          UDP length
 *
 *      Returns:
 *
 *      Pointer to constructed UDP header
 *
 *      This constructs a UDP header which is used for IKE
 *      encapsulated within TCP.
 */
unsigned char*make_udphdr(size_t *length, int sport, int dport,
			  unsigned udplen)
{
	unsigned char *payload;
	struct ike_udphdr *hdr;

	payload = Malloc(sizeof(struct ike_udphdr));
	hdr = (struct ike_udphdr*) payload; /* Overlay UDP hdr on payload */

	hdr->source = htons(sport);
	hdr->dest   = htons(dport);
	hdr->len    = htons(udplen);
	hdr->check  = 0; /* should use in_cksum() */

	*length = sizeof(struct ike_udphdr);

	return payload;
}

/*
 *	skip_payload -- Skip an ISAMKP payload
 *
 *	Inputs:
 *
 *	cp	Pointer to start of payload to skip
 *	len	Packet length remaining
 *	next	Next payload type.
 *
 *	Returns:
 *
 *	Pointer to start of next payload, or NULL if no next payload.
 */
unsigned char *skip_payload(unsigned char *cp, size_t *len, unsigned *next)
{
	struct isakmp_generic *hdr = (struct isakmp_generic *) cp;

/*
 *	Signal no more payloads by setting length to zero if:
 *
 *	The packet length is less than the ISAKMP generic header size; or
 *	The payload length is greater than the packet length; or
 *	The payload length is less than the size of the generic header; or
 *	There is no next payload.
 *
 *	Also set *next to none and return null.
 */
	if (*len < sizeof(struct isakmp_generic) ||
	    ntohs(hdr->isag_length) >= *len ||
	    ntohs(hdr->isag_length) < sizeof(struct isakmp_generic) ||
	    hdr->isag_np == ISAKMP_NEXT_NONE) {
		*len = 0;
		*next = ISAKMP_NEXT_NONE;
		return NULL;
	}
/*
 *	There is another payload after this one, so adjust length and
 *	return pointer to next payload.
 */
	*len = *len - ntohs(hdr->isag_length);
	*next = hdr->isag_np;
	return cp + ntohs(hdr->isag_length);
}

/*
 *	process_isakmp_hdr -- Process ISAKMP header
 *
 *	Inputs:
 *
 *	cp	Pointer to start of ISAKMP header
 *	len	Packet length remaining
 *	next	Next payload type.
 *	type	Exchange type
 *
 *	Returns:
 *
 *	Pointer to start of next payload, or NULL if no next payload.
 */
unsigned char *process_isakmp_hdr(unsigned char *cp, size_t *len,
				  unsigned *next,
				  unsigned *type)
{
	struct isakmp_hdr *hdr = (struct isakmp_hdr *) cp;

/*
 *	Signal no more payloads by setting length to zero if:
 *
 *	The packet length is less than the ISAKMP header size; or
 *	The payload length is less than the size of the header; or
 *	There is no next payload.
 *
 *	Also set *next to none and return null.
 */
	if (*len < sizeof(struct isakmp_hdr) ||
	    ntohl(hdr->isa_length) < sizeof(struct isakmp_hdr) ||
	    hdr->isa_np == ISAKMP_NEXT_NONE) {
		*len = 0;
		*next = ISAKMP_NEXT_NONE;
		*type = ISAKMP_XCHG_NONE;
		return NULL;
	}
/*
 *	There is another payload after this one, so adjust length and
 *	return pointer to next payload.
 */
	*len = *len - sizeof(struct isakmp_hdr);
	*next = hdr->isa_np;
	*type = hdr->isa_xchg;
	return cp + sizeof(struct isakmp_hdr);
}

/*
 *	process_sa -- Process SA Payload
 *
 *	Inputs:
 *
 *	cp	Pointer to start of SA payload
 *	len	Packet length remaining
 *	type	Exchange type.
 *	quiet	Only print the basic info if nonzero
 *	multiline	Split decodes across lines if nonzero
 *
 *	Returns:
 *
 *	Pointer to SA description string.
 *
 *	The description string pointer returned points to malloc'ed storage
 *	which should be free'ed by the caller when it's no longer needed.
 */
char *process_sa(unsigned char *cp, size_t len, unsigned type, int quiet,
		 int multiline)
{
	struct isakmp_sa *sa_hdr = (struct isakmp_sa *) cp;
	struct isakmp_proposal *prop_hdr =
		(struct isakmp_proposal *) (cp + sizeof(struct isakmp_sa));
	char *msg;
	char *msg2;
	char *msg3;
	unsigned char *attr_ptr;
	size_t safelen; /* Shorter of actual and claimed length */

	safelen = (ntohs(sa_hdr->isasa_length) < len) ? ntohs(
		sa_hdr->isasa_length) : len;
/*
 *	Return with a "too short to decode" message if either the remaining
 *	packet length or the claimed payload length is less than the combined
 *	size of the SA, Proposal, and transform headers.
 */
	if (safelen < sizeof(struct isakmp_sa) +
	    sizeof(struct isakmp_proposal) +
	    sizeof(struct isakmp_transform))
		return make_message(
			"IKE Handshake returned (packet too short to decode)");



/*
 *	Build the first part of the message based on the exchange type.
 */
	if (type == ISAKMP_XCHG_IDPROT) {       /* Main Mode */
		msg = make_message("Main Mode Handshake returned");
	} else if (type == ISAKMP_XCHG_AGGR) {  /* Aggressive Mode */
		msg = make_message("Aggressive Mode Handshake returned");
	} else {
		msg =
			make_message("UNKNOWN Mode Handshake returned (%u)",
				     type);
	}
/*
 *	We should have exactly one transform in the server's response.
 *	If there is not one transform, then add this fact to the message.
 */
	if (prop_hdr->isap_notrans != 1) {
		msg2 = msg;
		msg = make_message("%s (%d transforms)", msg2,
				   prop_hdr->isap_notrans);
		free(msg2);
	}
/*
 *	If quiet is not in effect, add the transform details to the message.
 */
	if (!quiet) {
		int firstloop = 1;

		msg2 = msg;
		msg = make_message("%s%sSA=(", msg2, multiline ? "\n\t" : " ");
		free(msg2);
		attr_ptr =
			(cp + sizeof(struct isakmp_sa) +
			 sizeof(struct isakmp_proposal) +
			 sizeof(struct isakmp_transform));
		safelen -= sizeof(struct isakmp_sa) +
			   sizeof(struct isakmp_proposal) +
			   sizeof(struct isakmp_transform);

		while (safelen) {
			msg2 = msg;
			msg3 = process_attr(&attr_ptr, &safelen);
			if (firstloop) { /* Don't need leading space for 1st attr */
				msg = make_message("%s%s", msg2, msg3);
				firstloop = 0;
			} else {
				msg = make_message("%s %s", msg2, msg3);
			}
			free(msg2);
			free(msg3);
		}
		msg2 = msg;
		msg = make_message("%s)", msg2);
		free(msg2);
	}

	return msg;
}

/*
 *	process_attr -- Process transform attribute
 *
 *	Inputs:
 *
 *	cp	Pointer to start of attribute
 *	len	Packet length remaining
 *
 *	Returns:
 *
 *	Pointer to attribute description string.
 *
 *	The description string pointer returned points to malloc'ed storage
 *	which should be free'ed by the caller when it's no longer needed.
 */
char *process_attr(unsigned char **cp, size_t *len)
{
	char *msg;
	struct isakmp_attribute *attr_hdr = (struct isakmp_attribute *) *cp;
	char attr_type; /* B=Basic, V=Variable */
	unsigned attr_class;
	unsigned attr_value = 0;
	char *attr_class_str;
	char *attr_value_str;
	size_t value_len;
	size_t size;
	static const char *attr_classes[] = {           /* From RFC 2409 App. A */
		NULL,                                   /*  0 */
		"Enc",                                  /*  1 */
		"Hash",                                 /*  2 */
		"Auth",                                 /*  3 */
		"Group",                                /*  4 */
		"GroupType",                            /*  5 */
		"GroupPrime/IrreduciblePolynomial",     /*  6 */
		"GroupGeneratorOne",                    /*  7 */
		"GroupGeneratorTwo",                    /*  8 */
		"GroupCurve A",                         /*  9 */
		"GroupCurve B",                         /* 10 */
		"LifeType",                             /* 11 */
		"LifeDuration",                         /* 12 */
		"PRF",                                  /* 13 */
		"KeyLength",                            /* 14 */
		"FieldSize",                            /* 15 */
		"GroupOrder"                            /* 16 */
	};
	static const char *enc_names[] = {              /* From RFC 2409 App. A */
		NULL,                                   /* and RFC 3602 */
		"DES",                                  /*  1 */
		"IDEA",                                 /*  2 */
		"Blowfish",                             /*  3 */
		"RC5",                                  /*  4 */
		"3DES",                                 /*  5 */
		"CAST",                                 /*  6 */
		"AES"                                   /*  7 */
	};
	static const char *hash_names[] = {             /* From RFC 2409 App. A */
		NULL,
		"MD5",                                  /*  1 */
		"SHA1",                                 /*  2 */
		"Tiger",                                /*  3 */
		"SHA2-256",                             /*  4 */
		"SHA2-384",                             /*  5 */
		"SHA2-512"                              /*  6 */
	};
	static const char *auth_names[] = {             /* From RFC 2409 App. A */
		NULL,
		"PSK",                                  /*  1 */
		"DSS",                                  /*  2 */
		"RSA_Sig",                              /*  3 */
		"RSA_Enc",                              /*  4 */
		"RSA_RevEnc",                           /*  5 */
		"ElGamel_Enc",                          /*  6 */
		"ElGamel_RevEnc",                       /*  7 */
		"ECDSA_Sig"                             /*  8 */
	};
	static const char *dh_names[] = {               /* From RFC 2409 App. A */
		NULL,                                   /* and RFC 3526 */
		"1:modp768",                            /*  1 */
		"2:modp1024",                           /*  2 */
		"3:ec2n155",                            /*  3 */
		"4:ec2n185",                            /*  4 */
		"5:modp1536",                           /*  5 */
		"6:ec2n163",                            /*  6 */
		"7:ec2n163",                            /*  7 */
		"8:ec2n283",                            /*  8 */
		"9:ec2n283",                            /*  9 */
		"10:ec2n409",                           /* 10 */
		"11:ec2n409",                           /* 11 */
		"12:ec2n571",                           /* 12 */
		"13:ec2n571",                           /* 13 */
		"14:modp2048",                          /* 14 */
		"15:modp3072",                          /* 15 */
		"16:modp4096",                          /* 16 */
		"17:modp6144",                          /* 17 */
		"18:modp8192"                           /* 18 */
	};
	static const char *life_names[] = {             /* From RFC 2409 App. A */
		NULL,
		"Seconds",                              /*  1 */
		"Kilobytes"                             /*  2 */
	};

	if (ntohs(attr_hdr->isaat_af_type) & 0x8000) {  /* Basic attribute */
		attr_type = 'B';
		attr_class = ntohs(attr_hdr->isaat_af_type) & 0x7fff;
		attr_value = ntohs(attr_hdr->isaat_lv);
		value_len = 0;  /* Value is in length field */
	} else {                /* Variable attribute */
		attr_type = 'V';
		attr_class = ntohs(attr_hdr->isaat_af_type);
		value_len = ntohs(attr_hdr->isaat_lv);
	}

	attr_class_str =
		make_message("%s", STR_OR_ID(attr_class, attr_classes));

	if (attr_type == 'B') {
		switch (attr_class) {
		case 1: /* Encryption Algorithm */
			attr_value_str =
				make_message("%s",
					     STR_OR_ID(attr_value, enc_names));
			break;
		case 2: /* Hash Algorithm */
			attr_value_str =
				make_message("%s",
					     STR_OR_ID(attr_value,
						       hash_names));
			break;
		case 3: /* Authentication Method */
			attr_value_str =
				make_message("%s",
					     STR_OR_ID(attr_value,
						       auth_names));
			break;
		case 4: /* Group Desription */
			attr_value_str =
				make_message("%s",
					     STR_OR_ID(attr_value, dh_names));
			break;
		case 11: /* Life Type */
			attr_value_str =
				make_message("%s",
					     STR_OR_ID(attr_value,
						       life_names));
			break;
		default:
			attr_value_str = make_message("%u", attr_value);
			break;
		}
	} else {
		attr_value_str =
			hexstring((*cp) + sizeof(struct isakmp_attribute),
				  value_len);
	}

	if (attr_type == 'B')
		msg = make_message("%s=%s", attr_class_str, attr_value_str);
	else
		msg = make_message("%s(%u)=0x%s", attr_class_str, value_len,
				   attr_value_str);

	free(attr_class_str);
	free(attr_value_str);

	size = sizeof(struct isakmp_attribute) + value_len;
	if (size >= *len) {
		*len = 0;
	} else {
		*len -= size;
		(*cp) += size;
	}

	return msg;
}

/*
 *	process_vid -- Process Vendor ID Payload
 *
 *	Inputs:
 *
 *	cp	Pointer to start of Vendor ID payload
 *	len	Packet length remaining
 *	vidlist	List of Vendor ID patterns.
 *
 *	Returns:
 *
 *	Pointer to Vendor ID description string.
 *
 *	The description string pointer returned points to malloc'ed storage
 *	which should be free'ed by the caller when it's no longer needed.
 */
char *process_vid(unsigned char *cp, size_t len,
		  struct vid_pattern_list *vidlist)
{
	struct isakmp_vid *hdr = (struct isakmp_vid *) cp;
	char *msg;
	char *hexvid;
	char *p;
	unsigned char *vid_data;
	size_t data_len;

#ifdef HAVE_REGEX_H
	struct vid_pattern_list *ve;
#endif

	if (len < sizeof(struct isakmp_vid) ||
	    ntohs(hdr->isavid_length) < sizeof(struct isakmp_vid))
		return make_message("VID (packet too short to decode)");

	vid_data = cp + sizeof(struct isakmp_vid); /* Points to start of VID data */
	data_len = ntohs(hdr->isavid_length) <
		   len ? ntohs(hdr->isavid_length) : len;
	data_len -= sizeof(struct isakmp_vid);

	hexvid = hexstring(vid_data, data_len);
	msg = make_message("VID=%s", hexvid);
/*
 *	Try to find a match in the Vendor ID pattern list.
 */
#ifdef HAVE_REGEX_H
	ve = vidlist;
	while (ve != NULL) {
		if (!(regexec(ve->regex, hexvid, 0, NULL, 0))) {
			p = msg;
			msg = make_message("%s (%s)", p, ve->name);
			free(p);
			break; /* Stop looking after first match */
		}
		ve = ve->next;
	}
#endif
	free(hexvid);
	return msg;
}

/*
 *	process_notify -- Process notify Payload
 *
 *	Inputs:
 *
 *	cp	Pointer to start of notify payload
 *	len	Packet length remaining
 *
 *	Returns:
 *
 *	Pointer to notify description string.
 *
 *	The description string pointer returned points to malloc'ed storage
 *	which should be free'ed by the caller when it's no longer needed.
 */
char *process_notify(unsigned char *cp, size_t len)
{
	struct isakmp_notification *hdr = (struct isakmp_notification *) cp;
	char *msg;
	unsigned msg_type;
	size_t msg_len;
	unsigned char *msg_data;
	char *notify_msg;
	static const char *notification_msg[] = {       /* From RFC 2408 3.14.1 */
		"UNSPECIFIED",                          /* 0 */
		"INVALID-PAYLOAD-TYPE",                 /* 1 */
		"DOI-NOT-SUPPORTED",                    /* 2 */
		"SITUATION-NOT-SUPPORTED",              /* 3 */
		"INVALID-COOKIE",                       /* 4 */
		"INVALID-MAJOR-VERSION",                /* 5 */
		"INVALID-MINOR-VERSION",                /* 6 */
		"INVALID-EXCHANGE-TYPE",                /* 7 */
		"INVALID-FLAGS",                        /* 8 */
		"INVALID-MESSAGE-ID",                   /* 9 */
		"INVALID-PROTOCOL-ID",                  /* 10 */
		"INVALID-SPI",                          /* 11 */
		"INVALID-TRANSFORM-ID",                 /* 12 */
		"ATTRIBUTES-NOT-SUPPORTED",             /* 13 */
		"NO-PROPOSAL-CHOSEN",                   /* 14 */
		"BAD-PROPOSAL-SYNTAX",                  /* 15 */
		"PAYLOAD-MALFORMED",                    /* 16 */
		"INVALID-KEY-INFORMATION",              /* 17 */
		"INVALID-ID-INFORMATION",               /* 18 */
		"INVALID-CERT-ENCODING",                /* 19 */
		"INVALID-CERTIFICATE",                  /* 20 */
		"CERT-TYPE-UNSUPPORTED",                /* 21 */
		"INVALID-CERT-AUTHORITY",               /* 22 */
		"INVALID-HASH-INFORMATION",             /* 23 */
		"AUTHENTICATION-FAILED",                /* 24 */
		"INVALID-SIGNATURE",                    /* 25 */
		"ADDRESS-NOTIFICATION",                 /* 26 */
		"NOTIFY-SA-LIFETIME",                   /* 27 */
		"CERTIFICATE-UNAVAILABLE",              /* 28 */
		"UNSUPPORTED-EXCHANGE-TYPE",            /* 29 */
		"UNEQUAL-PAYLOAD-LENGTHS"               /* 30 */
	};

	if (len < sizeof(struct isakmp_notification) ||
	    ntohs(hdr->isan_length) < sizeof(struct isakmp_notification))
		return make_message(
			"Notify message (packet too short to decode)");

	msg_type = ntohs(hdr->isan_type);
	msg_len = ntohs(hdr->isan_length) - sizeof(struct isakmp_notification);
	msg_data = cp + sizeof(struct isakmp_notification);

	if (msg_type == 9101) { /* Firewall-1 4.x/NG Base message type */
		notify_msg = printable(msg_data, msg_len);
		msg = make_message(
			"Notify message %u (Firewall-1) Message=\"%s\"",
			msg_type, notify_msg);
		free(notify_msg);
	} else {                /* All other Message Types */
		msg = make_message("Notify message %u (%s)", msg_type,
				   STR_OR_ID(msg_type, notification_msg));
	}

	return msg;
}

/*
 *	process_id -- Process identification Payload
 *
 *	Inputs:
 *
 *	cp	Pointer to start of identification payload
 *	len	Packet length remaining
 *
 *	Returns:
 *
 *	Pointer to identification description string.
 *
 *	The description string pointer returned points to malloc'ed storage
 *	which should be free'ed by the caller when it's no longer needed.
 */
char *process_id(unsigned char *cp, size_t len)
{
	struct isakmp_id *hdr = (struct isakmp_id *) cp;
	unsigned idtype;
	char *msg;
	char *msg2;
	unsigned char *id_data;
	size_t data_len;
	static const char *id_names[] = {       /* From RFC 2407 4.6.2.1 */
		NULL,                           /*  0 */
		"ID_IPV4_ADDR",                 /*  1 */
		"ID_FQDN",                      /*  2 */
		"ID_USER_FQDN",                 /*  3 */
		"ID_IPV4_ADDR_SUBNET",          /*  4 */
		"ID_IPV6_ADDR",                 /*  5 */
		"ID_IPV6_ADDR_SUBNET",          /*  6 */
		"ID_IPV4_ADDR_RANGE",           /*  7 */
		"ID_IPV6_ADDR_RANGE",           /*  8 */
		"ID_DER_ASN1_DN",               /*  9 */
		"ID_DER_ASN1_GN",               /* 10 */
		"ID_KEY_ID",                    /* 11 */
	};

	if (len < sizeof(struct isakmp_id) ||
	    ntohs(hdr->isaid_length) < sizeof(struct isakmp_id))
		return make_message("ID (packet too short to decode)");

	id_data = cp + sizeof(struct isakmp_id); /* Points to start of ID data */
	data_len = ntohs(hdr->isaid_length) <
		   len ? ntohs(hdr->isaid_length) : len;
	data_len -= sizeof(struct isakmp_id);
	idtype = hdr->isaid_idtype;

	switch (idtype) {
		char *id;               /* Printable ID */
		struct in_addr in;      /* IPv4 Address */
		struct in_addr in2;     /* IPv4 Address */
		unsigned char *mask;    /* Netmask */

	case ID_IPV4_ADDR:
		if (data_len >= sizeof(struct in_addr)) {
			memcpy(&in, id_data, sizeof(struct in_addr));
			msg = make_message("Value=%s", inet_ntoa(in));
		} else {
			msg = make_message("Value too short to decode");
		}
		break;
	case ID_IPV4_ADDR_SUBNET:
		if (data_len >= sizeof(struct in_addr) + 4) {
			memcpy(&in, id_data, sizeof(struct in_addr));
			mask = id_data + sizeof(struct in_addr);
			msg = make_message("Value=%s/%u.%u.%u.%u", inet_ntoa(
						   in),
					   mask[0], mask[1], mask[2], mask[3]);
		} else {
			msg = make_message("Value too short to decode");
		}
		break;
	case ID_IPV4_ADDR_RANGE:
		if (data_len >=  2 * sizeof(struct in_addr)) {
			memcpy(&in, id_data, sizeof(struct in_addr));
			memcpy(&in2, id_data + sizeof(struct in_addr),
			       sizeof(struct in_addr));
			msg = make_message("Value=%s-%s", inet_ntoa(
						   in), inet_ntoa(in2));
		} else {
			msg = make_message("Value too short to decode");
		}
		break;
	case ID_FQDN:
	case ID_USER_FQDN:
		id = printable(id_data, data_len);
		msg = make_message("Value=%s", id);
		free(id);
		break;
	case ID_KEY_ID:
		id = hexstring(id_data, data_len);
		msg = make_message("Value=%s", id);
		free(id);
		break;
	case ID_IPV6_ADDR:
	case ID_IPV6_ADDR_SUBNET:
	case ID_IPV6_ADDR_RANGE:
	case ID_DER_ASN1_DN:
	case ID_DER_ASN1_GN:
		msg = make_message("Decode not supported for this type");
		break;
	default:
		msg = make_message("Unknown ID Type");
		break;
	}

	msg2 = msg;
	msg =
		make_message("ID(Type=%s, %s)", STR_OR_ID(idtype,
							  id_names), msg2);
	free(msg2);

	return msg;
}

/*
 *	print_payload -- Print an ISAKMP payload in hex
 *
 *	Inputs:
 *
 *	cp	Pointer to start of ISAKMP payload
 *	payload	Numeric value of this payload type, 0 = ISAKMP header
 *	dir	Direction: 'I' for initiator or 'R' for responder
 *
 *	Returns:
 *
 *	None
 *
 *	This function is used for debugging.  It trusts the length in the
 *	generic ISAKMP header, and so could misbehave with corrupted packets.
 */
void print_payload(unsigned char *cp, unsigned payload, int dir)
{
	struct isakmp_generic *hdr = (struct isakmp_generic *) cp;
	struct isakmp_hdr *ihdr = (struct isakmp_hdr *) cp;
	char *hexdata;
	unsigned char *data;
	size_t data_len;

	if (payload) {                                          /* Some other payload */
		data = cp + sizeof(struct isakmp_generic);      /* Points to start of data */
		data_len = ntohs(hdr->isag_length);
		data_len -= sizeof(struct isakmp_generic);
		hexdata = hexstring(data, data_len);
		switch (payload) {
		case ISAKMP_NEXT_SA:
			printf("sa%c_b_hex=\"%s\"\n", (dir == 'I') ? 'i' : 'r',
			       hexdata);
			break;
		case ISAKMP_NEXT_KE:
			printf("g_x%c_hex=\"%s\"\n", (dir == 'I') ? 'i' : 'r',
			       hexdata);
			break;
		case ISAKMP_NEXT_ID:
			printf("idi%c_b_hex=\"%s\"\n",
			       (dir == 'I') ? 'i' : 'r', hexdata);
			break;
		case ISAKMP_NEXT_HASH:
			printf("expected_hash_%c_hex=\"%s\"\n",
			       (dir == 'I') ? 'i' : 'r', hexdata);
			break;
		case ISAKMP_NEXT_NONCE:
			printf("n%c_b_hex=\"%s\"\n", (dir == 'I') ? 'i' : 'r',
			       hexdata);
			break;
		default:
			printf("UNKNOWN PAYLOAD TYPE: %d\n", payload);
			break;
		}
		free(hexdata);
	} else { /* ISAKMP Header */
		hexdata = hexstring((unsigned char *)ihdr->isa_icookie, 8);
		printf("cky_i_hex=\"%s\"\n", hexdata);
		free(hexdata);
		hexdata = hexstring((unsigned char *)ihdr->isa_rcookie, 8);
		printf("cky_r_hex=\"%s\"\n", hexdata);
		free(hexdata);
	}
}

/*
 *	add_psk_crack_payload -- Add an ISAKMP payload to PSK crack structure
 *
 *	Inputs:
 *
 *	cp	Pointer to start of ISAKMP payload
 *	payload	Numeric value of this payload type, 0 = ISAKMP header
 *	dir	Direction: 'I' for initiator or 'R' for responder
 *
 *	Returns:
 *
 *	None
 *
 *	This function is used for debugging.  It trusts the length in the
 *	generic ISAKMP header, and so could misbehave with corrupted packets.
 */
void add_psk_crack_payload(unsigned char *cp, unsigned payload, int dir)
{
	struct isakmp_generic *hdr = (struct isakmp_generic *) cp;
	struct isakmp_hdr *ihdr = (struct isakmp_hdr *) cp;
	unsigned char *data;
	size_t data_len;

	if (payload) {  /* Some other payload */
		data_len = ntohs(hdr->isag_length) -
			   sizeof(struct isakmp_generic);
		data = Malloc(data_len);
		memcpy(data, cp + sizeof(struct isakmp_generic), data_len);

		switch (payload) {
		case ISAKMP_NEXT_SA:
			if (dir == 'I') {
				psk_values.sai_b = data;
				psk_values.sai_b_len = data_len;
			}
			break;
		case ISAKMP_NEXT_KE:
			if (dir == 'I') {
				psk_values.g_xi = data;
				psk_values.g_xi_len = data_len;
			} else {
				psk_values.g_xr = data;
				psk_values.g_xr_len = data_len;
			}
			break;
		case ISAKMP_NEXT_ID:
			if (dir == 'R') {
				psk_values.idir_b = data;
				psk_values.idir_b_len = data_len;
			}
			break;
		case ISAKMP_NEXT_HASH:
			if (dir == 'R') {
				psk_values.hash_r = data;
				psk_values.hash_r_len = data_len;
			}
			break;
		case ISAKMP_NEXT_NONCE:
			if (dir == 'I') {
				psk_values.ni_b = data;
				psk_values.ni_b_len = data_len;
			} else {
				psk_values.nr_b = data;
				psk_values.nr_b_len = data_len;
			}
			break;
		default:
			warn_msg(
				"add_psk_crack_payload: UNKNOWN PAYLOAD TYPE: %d\n",
				payload);
			break;
		}
	} else {                /* ISAKMP Header */
		data_len = 8;   /* ISAKMP cookies are 8 bytes long */
		data = Malloc(data_len);
		memcpy(data, (unsigned char *)ihdr->isa_rcookie, 8);
		psk_values.cky_r = data;
		psk_values.cky_r_len = data_len;

		data = Malloc(data_len);
		memcpy(data, (unsigned char *)ihdr->isa_icookie, 8);
		psk_values.cky_i = data;
		psk_values.cky_i_len = data_len;
	}
}

/*
 *	print_psk_crack_values -- Display the PSK crack values
 *
 *	Inputs:
 *
 *	psk_crack_file	Name of PSK data output file, or NULL for stdout
 *
 *	Returns:
 *
 *	None
 *
 *	This function prints the PSK crack values in the following format:
 *
 *	g_xr:g_xi:cky_r:cky_i:sai_b:idir_b:ni_b:nr_b:hash_r
 */
void print_psk_crack_values(const char *psk_crack_file)
{
	char *hexdata;
	FILE *fp;

	if (psk_crack_file[0]) {
		if ((fp = fopen(psk_crack_file, "w")) == NULL)
			err_sys("ERROR: fopen");
	} else {
		fp = stdout;
		printf("IKE PSK parameters (g_xr:g_xi:cky_r:cky_i:sai_b:idir_b:ni_b:nr_b:hash_r):\n");
	}

	hexdata = hexstring(psk_values.g_xr, psk_values.g_xr_len);
	fprintf(fp, "%s:", hexdata);
	free(hexdata);
	hexdata = hexstring(psk_values.g_xi, psk_values.g_xi_len);
	fprintf(fp, "%s:", hexdata);
	free(hexdata);
	hexdata = hexstring(psk_values.cky_r, psk_values.cky_r_len);
	fprintf(fp, "%s:", hexdata);
	free(hexdata);
	hexdata = hexstring(psk_values.cky_i, psk_values.cky_i_len);
	fprintf(fp, "%s:", hexdata);
	free(hexdata);
	hexdata = hexstring(psk_values.sai_b, psk_values.sai_b_len);
	fprintf(fp, "%s:", hexdata);
	free(hexdata);
	hexdata = hexstring(psk_values.idir_b, psk_values.idir_b_len);
	fprintf(fp, "%s:", hexdata);
	free(hexdata);
	hexdata = hexstring(psk_values.ni_b, psk_values.ni_b_len);
	fprintf(fp, "%s:", hexdata);
	free(hexdata);
	hexdata = hexstring(psk_values.nr_b, psk_values.nr_b_len);
	fprintf(fp, "%s:", hexdata);
	free(hexdata);
	hexdata = hexstring(psk_values.hash_r, psk_values.hash_r_len);
	fprintf(fp, "%s\n", hexdata);
	free(hexdata);
}

void isakmp_use_rcsid(void)
{
	fprintf(stderr, "%s\n", rcsid); /* Use rcsid to stop compiler optimising away */
}
