/*	$NetBSD: isakmp_xauth.c,v 1.11.6.2 2009/04/20 13:35:36 tteras Exp $	*/

/* Id: isakmp_xauth.c,v 1.38 2006/08/22 18:17:17 manubsd Exp */

/*
 * Copyright (C) 2004-2005 Emmanuel Dreyfus
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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.
 */

#include "config.h"

#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/queue.h>

#include <netinet/in.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif
#include <netdb.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <ctype.h>
#include <resolv.h>

#ifdef HAVE_SHADOW_H
#include <shadow.h>
#endif

#include "var.h"
#include "misc.h"
#include "vmbuf.h"
#include "plog.h"
#include "sockmisc.h"
#include "schedule.h"
#include "debug.h"

#include "crypto_openssl.h"
#include "isakmp_var.h"
#include "isakmp.h"
#include "admin.h"
#include "privsep.h"
#include "evt.h"
#include "handler.h"
#include "throttle.h"
#include "remoteconf.h"
#include "isakmp_inf.h"
#include "isakmp_xauth.h"
#include "isakmp_unity.h"
#include "isakmp_cfg.h"
#include "strnames.h"
#include "ipsec_doi.h"
#include "remoteconf.h"
#include "localconf.h"

#ifdef HAVE_LIBRADIUS
#include <radlib.h>

struct rad_handle *radius_auth_state = NULL;
struct rad_handle *radius_acct_state = NULL;
#endif

#ifdef HAVE_LIBPAM
#include <security/pam_appl.h>

static char *PAM_usr = NULL;
static char *PAM_pwd = NULL;
static int PAM_conv(int, const struct pam_message **, 
    struct pam_response **, void *);
static struct pam_conv PAM_chat = { &PAM_conv, NULL };
#endif

#ifdef HAVE_LIBLDAP
#include "ldap.h"
#include <arpa/inet.h>
struct xauth_ldap_config xauth_ldap_config;
#endif

void 
xauth_sendreq(iph1)
	struct ph1handle *iph1;
{
	vchar_t *buffer;
	struct isakmp_pl_attr *attr;
	struct isakmp_data *typeattr;
	struct isakmp_data *usrattr;
	struct isakmp_data *pwdattr;
	struct xauth_state *xst = &iph1->mode_cfg->xauth;
	size_t tlen;

	/* Status checks */
	if (iph1->status != PHASE1ST_ESTABLISHED) {
		plog(LLV_ERROR, LOCATION, NULL, 
		    "Xauth request while phase 1 is not completed\n");
		return;
	}

	if (xst->status != XAUTHST_NOTYET) {
		plog(LLV_ERROR, LOCATION, NULL, 
		    "Xauth request whith Xauth state %d\n", xst->status);
		return;
	}

	plog(LLV_INFO, LOCATION, NULL, "Sending Xauth request\n");

	tlen = sizeof(*attr) +
	       + sizeof(*typeattr) +
	       + sizeof(*usrattr) +
	       + sizeof(*pwdattr);
	
	if ((buffer = vmalloc(tlen)) == NULL) {
		plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate buffer\n");
		return;
	}
	
	attr = (struct isakmp_pl_attr *)buffer->v;
	memset(attr, 0, tlen);

	attr->h.len = htons(tlen);
	attr->type = ISAKMP_CFG_REQUEST;
	attr->id = htons(eay_random());

	typeattr = (struct isakmp_data *)(attr + 1);
	typeattr->type = htons(XAUTH_TYPE | ISAKMP_GEN_TV);
	typeattr->lorv = htons(XAUTH_TYPE_GENERIC);

	usrattr = (struct isakmp_data *)(typeattr + 1);
	usrattr->type = htons(XAUTH_USER_NAME | ISAKMP_GEN_TLV);
	usrattr->lorv = htons(0);

	pwdattr = (struct isakmp_data *)(usrattr + 1);
	pwdattr->type = htons(XAUTH_USER_PASSWORD | ISAKMP_GEN_TLV);
	pwdattr->lorv = htons(0);

	isakmp_cfg_send(iph1, buffer, 
	    ISAKMP_NPTYPE_ATTR, ISAKMP_FLAG_E, 1);
	
	vfree(buffer);

	xst->status = XAUTHST_REQSENT;

	return;
}

int
xauth_attr_reply(iph1, attr, id)
	struct ph1handle *iph1;
	struct isakmp_data *attr;
	int id;
{
	char **outlet = NULL;
	size_t alen = 0;
	int type;
	struct xauth_state *xst = &iph1->mode_cfg->xauth;

	if ((iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_XAUTH) == 0) {
		plog(LLV_ERROR, LOCATION, NULL, 
		    "Xauth reply but peer did not declare "
		    "itself as Xauth capable\n");
		return -1;
	}

	if (xst->status != XAUTHST_REQSENT) {
		plog(LLV_ERROR, LOCATION, NULL, 
		    "Xauth reply while Xauth state is %d\n", xst->status);
		return -1;
	}

	type = ntohs(attr->type) & ~ISAKMP_GEN_MASK;
	switch (type) {
	case XAUTH_TYPE:
		switch (ntohs(attr->lorv)) {
		case XAUTH_TYPE_GENERIC:
			xst->authtype = XAUTH_TYPE_GENERIC;
			break;
		default:
			plog(LLV_WARNING, LOCATION, NULL, 
			    "Unexpected authentication type %d\n", 
			    ntohs(type));
			return -1;
		}
		break;

	case XAUTH_USER_NAME:
		outlet = &xst->authdata.generic.usr;
		break;

	case XAUTH_USER_PASSWORD:
		outlet = &xst->authdata.generic.pwd; 
		break;

	default:
		plog(LLV_WARNING, LOCATION, NULL, 
		    "ignored Xauth attribute %d\n", type);
		break;
	}

	if (outlet != NULL) {
		alen = ntohs(attr->lorv);

		if ((*outlet = racoon_malloc(alen + 1)) == NULL) {
			plog(LLV_ERROR, LOCATION, NULL, 
			    "Cannot allocate memory for Xauth Data\n");
			return -1;
		}

		memcpy(*outlet, attr + 1, alen);
		(*outlet)[alen] = '\0';
		outlet = NULL;
	}

	
	if ((xst->authdata.generic.usr != NULL) &&
	   (xst->authdata.generic.pwd != NULL)) {
		int port;
		int res;
		char *usr = xst->authdata.generic.usr;
		char *pwd = xst->authdata.generic.pwd;
		time_t throttle_delay = 0;

#if 0	/* Real debug, don't do that at home */
		plog(LLV_DEBUG, LOCATION, NULL, 
		    "Got username \"%s\", password \"%s\"\n", usr, pwd);
#endif
		strncpy(iph1->mode_cfg->login, usr, LOGINLEN);
		iph1->mode_cfg->login[LOGINLEN] = '\0';

		res = -1;
		if ((port = isakmp_cfg_getport(iph1)) == -1) {
			plog(LLV_ERROR, LOCATION, NULL, 
			    "Port pool depleted\n");
			goto skip_auth;
		}	

		switch (isakmp_cfg_config.authsource) {
		case ISAKMP_CFG_AUTH_SYSTEM:
			res = privsep_xauth_login_system(usr, pwd);
			break;
#ifdef HAVE_LIBRADIUS
		case ISAKMP_CFG_AUTH_RADIUS:
			res = xauth_login_radius(iph1, usr, pwd);
			break;
#endif
#ifdef HAVE_LIBPAM
		case ISAKMP_CFG_AUTH_PAM:
			res = privsep_xauth_login_pam(iph1->mode_cfg->port, 
			    iph1->remote, usr, pwd);
			break;
#endif
#ifdef HAVE_LIBLDAP
		case ISAKMP_CFG_AUTH_LDAP:
			res = xauth_login_ldap(iph1, usr, pwd);
			break;
#endif
		default:
			plog(LLV_ERROR, LOCATION, NULL, 
			    "Unexpected authentication source\n");
			res = -1;
			break;
		}

		/*
		 * Optional group authentication
		 */
		if (!res && (isakmp_cfg_config.groupcount))
			res = group_check(iph1,
				isakmp_cfg_config.grouplist,
				isakmp_cfg_config.groupcount);

		/*
		 * On failure, throttle the connexion for the remote host
		 * in order to make password attacks more difficult.
		 */
		throttle_delay = throttle_host(iph1->remote, res) - time(NULL);
		if (throttle_delay > 0) {
			char *str;

			str = saddrwop2str(iph1->remote);

			plog(LLV_ERROR, LOCATION, NULL, 
			    "Throttling in action for %s: delay %lds\n",
			    str, (unsigned long)throttle_delay);
			res = -1;
		} else {
			throttle_delay = 0;
		}

skip_auth:
		if (throttle_delay != 0) {
			struct xauth_reply_arg *xra;

			if ((xra = racoon_malloc(sizeof(*xra))) == NULL) {
				plog(LLV_ERROR, LOCATION, NULL, 
				    "malloc failed, bypass throttling\n");
				return xauth_reply(iph1, port, id, res);
			}

			/*
			 * We need to store the ph1, but it might have
			 * disapeared when xauth_reply is called, so
			 * store the index instead.
			 */
			xra->index = iph1->index;
			xra->port = port;
			xra->id = id;
			xra->res = res;
			sched_new(throttle_delay, xauth_reply_stub, xra);
		} else {
			return xauth_reply(iph1, port, id, res);
		}
	}

	return 0;
}

void 
xauth_reply_stub(args)
	void *args;
{
	struct xauth_reply_arg *xra = (struct xauth_reply_arg *)args;
	struct ph1handle *iph1;

	if ((iph1 = getph1byindex(&xra->index)) != NULL)
		(void)xauth_reply(iph1, xra->port, xra->id, xra->res);
	else
		plog(LLV_ERROR, LOCATION, NULL, 
		    "Delayed Xauth reply: phase 1 no longer exists.\n"); 

	racoon_free(xra);
	return;
}

int
xauth_reply(iph1, port, id, res)
	struct ph1handle *iph1;
	int port;
	int id;
{
	struct xauth_state *xst = &iph1->mode_cfg->xauth;
	char *usr = xst->authdata.generic.usr;

	if (res != 0) {
		if (port != -1)
			isakmp_cfg_putport(iph1, port);

		plog(LLV_INFO, LOCATION, NULL, 
		    "login failed for user \"%s\"\n", usr);
		
		xauth_sendstatus(iph1, XAUTH_STATUS_FAIL, id);
		xst->status = XAUTHST_NOTYET;

		/* Delete Phase 1 SA */
		if (iph1->status == PHASE1ST_ESTABLISHED)
			isakmp_info_send_d1(iph1);
		remph1(iph1);
		delph1(iph1);

		return -1;
	}

	xst->status = XAUTHST_OK;
	plog(LLV_INFO, LOCATION, NULL, 
	    "login succeeded for user \"%s\"\n", usr);

	xauth_sendstatus(iph1, XAUTH_STATUS_OK, id);

	return 0;
}

void
xauth_sendstatus(iph1, status, id)
	struct ph1handle *iph1;
	int status;
	int id;
{
	vchar_t *buffer;
	struct isakmp_pl_attr *attr;
	struct isakmp_data *stattr;
	size_t tlen;

	tlen = sizeof(*attr) +
	       + sizeof(*stattr); 
	
	if ((buffer = vmalloc(tlen)) == NULL) {
		plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate buffer\n");
		return;
	}
	
	attr = (struct isakmp_pl_attr *)buffer->v;
	memset(attr, 0, tlen);

	attr->h.len = htons(tlen);
	attr->type = ISAKMP_CFG_SET;
	attr->id = htons(id);

	stattr = (struct isakmp_data *)(attr + 1);
	stattr->type = htons(XAUTH_STATUS | ISAKMP_GEN_TV);
	stattr->lorv = htons(status);

	isakmp_cfg_send(iph1, buffer, 
	    ISAKMP_NPTYPE_ATTR, ISAKMP_FLAG_E, 1);
	
	vfree(buffer);

	return;	
}

#ifdef HAVE_LIBRADIUS
int
xauth_radius_init(void)
{
	/* For first time use, initialize Radius */
	if ((isakmp_cfg_config.authsource == ISAKMP_CFG_AUTH_RADIUS) &&
	    (radius_auth_state == NULL)) {
		if ((radius_auth_state = rad_auth_open()) == NULL) {
			plog(LLV_ERROR, LOCATION, NULL, 
			    "Cannot init libradius\n");
			return -1;
		}

		if (rad_config(radius_auth_state, NULL) != 0) {
			plog(LLV_ERROR, LOCATION, NULL, 
			    "Cannot open librarius config file: %s\n", 
			    rad_strerror(radius_auth_state));
			rad_close(radius_auth_state);
			radius_auth_state = NULL;
			return -1;
		}
	}

	if ((isakmp_cfg_config.accounting == ISAKMP_CFG_ACCT_RADIUS) &&
	    (radius_acct_state == NULL)) {
		if ((radius_acct_state = rad_acct_open()) == NULL) {
			plog(LLV_ERROR, LOCATION, NULL, 
			    "Cannot init libradius\n");
			return -1;
		}

		if (rad_config(radius_acct_state, NULL) != 0) {
			plog(LLV_ERROR, LOCATION, NULL, 
			    "Cannot open librarius config file: %s\n", 
			    rad_strerror(radius_acct_state));
			rad_close(radius_acct_state);
			radius_acct_state = NULL;
			return -1;
		}
	}

	return 0;
}

int
xauth_login_radius(iph1, usr, pwd)
	struct ph1handle *iph1;
	char *usr;
	char *pwd;
{
	int res;
	const void *data;
	size_t len;
	int type;

	if (rad_create_request(radius_auth_state, RAD_ACCESS_REQUEST) != 0) {
		plog(LLV_ERROR, LOCATION, NULL, 
		    "rad_create_request failed: %s\n", 
		    rad_strerror(radius_auth_state));
		return -1;
	}
	
	if (rad_put_string(radius_auth_state, RAD_USER_NAME, usr) != 0) {
		plog(LLV_ERROR, LOCATION, NULL, 
		    "rad_put_string failed: %s\n", 
		    rad_strerror(radius_auth_state));
		return -1;
	}

	if (rad_put_string(radius_auth_state, RAD_USER_PASSWORD, pwd) != 0) {
		plog(LLV_ERROR, LOCATION, NULL, 
		    "rad_put_string failed: %s\n", 
		    rad_strerror(radius_auth_state));
		return -1;
	}

	if (isakmp_cfg_radius_common(radius_auth_state, iph1->mode_cfg->port) != 0)
		return -1;

	switch (res = rad_send_request(radius_auth_state)) {
	case RAD_ACCESS_ACCEPT:
		while ((type = rad_get_attr(radius_auth_state, &data, &len)) != 0) {
			switch (type) {
			case RAD_FRAMED_IP_ADDRESS:
				iph1->mode_cfg->addr4 = rad_cvt_addr(data);
				iph1->mode_cfg->flags 
				    |= ISAKMP_CFG_ADDR4_EXTERN;
				break;

			case RAD_FRAMED_IP_NETMASK:
				iph1->mode_cfg->mask4 = rad_cvt_addr(data);
				iph1->mode_cfg->flags 
				    |= ISAKMP_CFG_MASK4_EXTERN;
				break;

			default:
				plog(LLV_INFO, LOCATION, NULL,
				    "Unexpected attribute: %d\n", type);
				break;
			}
		}

		return 0;
		break;

	case RAD_ACCESS_REJECT:
		return -1;
		break;

	case -1:
		plog(LLV_ERROR, LOCATION, NULL, 
		    "rad_send_request failed: %s\n", 
		    rad_strerror(radius_auth_state));
		return -1;
		break;
	default:
		plog(LLV_ERROR, LOCATION, NULL, 
		    "rad_send_request returned %d\n", res);
		return -1;
		break;
	}

	return -1;
}
#endif

#ifdef HAVE_LIBPAM
static int 
PAM_conv(msg_count, msg, rsp, dontcare)
	int msg_count;
	const struct pam_message **msg;
	struct pam_response **rsp;
	void *dontcare;
{
	int i;
	int replies = 0;
	struct pam_response *reply = NULL;

	if ((reply = racoon_malloc(sizeof(*reply) * msg_count)) == NULL) 
		return PAM_CONV_ERR;
	bzero(reply, sizeof(*reply) * msg_count);

	for (i = 0; i < msg_count; i++) {
		switch (msg[i]->msg_style) {
		case PAM_PROMPT_ECHO_ON:
			/* Send the username, libpam frees resp */
			reply[i].resp_retcode = PAM_SUCCESS;
			if ((reply[i].resp = strdup(PAM_usr)) == NULL) {
				plog(LLV_ERROR, LOCATION, 
				    NULL, "strdup failed\n");
				exit(1);
			}
			break;

		case PAM_PROMPT_ECHO_OFF:
			/* Send the password, libpam frees resp */
			reply[i].resp_retcode = PAM_SUCCESS;
			if ((reply[i].resp = strdup(PAM_pwd)) == NULL) {
				plog(LLV_ERROR, LOCATION, 
				    NULL, "strdup failed\n");
				exit(1);
			}
			break;

		case PAM_TEXT_INFO:
		case PAM_ERROR_MSG:
			reply[i].resp_retcode = PAM_SUCCESS;
			reply[i].resp = NULL;
			break;

		default:
			if (reply != NULL)
				racoon_free(reply);
			return PAM_CONV_ERR;
			break;
		}
	}

	if (reply != NULL)
		*rsp = reply;

	return PAM_SUCCESS;
}

int
xauth_login_pam(port, raddr, usr, pwd)
	int port;
	struct sockaddr *raddr;
	char *usr;
	char *pwd;
{
	int error;
	int res;
	const void *data;
	size_t len;
	int type;
	char *remote = NULL;
	pam_handle_t *pam = NULL;

	if (isakmp_cfg_config.port_pool == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
		    "isakmp_cfg_config.port_pool == NULL\n");
		return -1;
	}

	if ((error = pam_start("racoon", usr, 
	    &PAM_chat, &isakmp_cfg_config.port_pool[port].pam)) != 0) {
		if (isakmp_cfg_config.port_pool[port].pam == NULL) {
			plog(LLV_ERROR, LOCATION, NULL, "pam_start failed\n");
			return -1;
		} else {
			plog(LLV_ERROR, LOCATION, NULL, 
			    "pam_start failed: %s\n", 
			    pam_strerror(isakmp_cfg_config.port_pool[port].pam,
			    error));
			goto out;
		}
	}
	pam = isakmp_cfg_config.port_pool[port].pam;

	if ((remote = strdup(saddrwop2str(raddr))) == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
		    "cannot allocate memory: %s\n", strerror(errno)); 
		goto out;
	}
	
	if ((error = pam_set_item(pam, PAM_RHOST, remote)) != 0) {
		plog(LLV_ERROR, LOCATION, NULL, 
		    "pam_set_item failed: %s\n", 
		    pam_strerror(pam, error));
		goto out;
	}

	PAM_usr = usr;
	PAM_pwd = pwd;
	error = pam_authenticate(pam, 0);
	PAM_usr = NULL;
	PAM_pwd = NULL;
	if (error != 0) {
		plog(LLV_ERROR, LOCATION, NULL, 
		    "pam_authenticate failed: %s\n", 
		    pam_strerror(pam, error));
		goto out;
	}

	if ((error = pam_acct_mgmt(pam, 0)) != 0) {
		plog(LLV_ERROR, LOCATION, NULL, 
		    "pam_acct_mgmt failed: %s\n", 
		    pam_strerror(pam, error));
		goto out;
	}

	if ((error = pam_setcred(pam, 0)) != 0) {
		plog(LLV_ERROR, LOCATION, NULL, 
		    "pam_setcred failed: %s\n", 
		    pam_strerror(pam, error));
		goto out;
	}

	if (remote != NULL)
		free(remote);

	return 0;

out:
	pam_end(pam, error);
	isakmp_cfg_config.port_pool[port].pam = NULL;
	if (remote != NULL)
		free(remote);
	return -1;
}
#endif

#ifdef HAVE_LIBLDAP
int 
xauth_ldap_init(void)
{
	int tmplen;
	int error = -1;

	xauth_ldap_config.pver = 3;
	xauth_ldap_config.host = NULL;
	xauth_ldap_config.port = LDAP_PORT;
	xauth_ldap_config.base = NULL;
	xauth_ldap_config.subtree = 0;
	xauth_ldap_config.bind_dn = NULL;
	xauth_ldap_config.bind_pw = NULL;
	xauth_ldap_config.auth_type = LDAP_AUTH_SIMPLE;
	xauth_ldap_config.attr_user = NULL;
	xauth_ldap_config.attr_addr = NULL;
	xauth_ldap_config.attr_mask = NULL;
	xauth_ldap_config.attr_group = NULL;
	xauth_ldap_config.attr_member = NULL;

	/* set default host */
	tmplen = strlen(LDAP_DFLT_HOST);
	xauth_ldap_config.host = vmalloc(tmplen);
	if (xauth_ldap_config.host == NULL)
		goto out;
	memcpy(xauth_ldap_config.host->v, LDAP_DFLT_HOST, tmplen);

	/* set default user naming attribute */
	tmplen = strlen(LDAP_DFLT_USER);
	xauth_ldap_config.attr_user = vmalloc(tmplen);
	if (xauth_ldap_config.attr_user == NULL)
		goto out;	
	memcpy(xauth_ldap_config.attr_user->v, LDAP_DFLT_USER, tmplen);

	/* set default address attribute */
	tmplen = strlen(LDAP_DFLT_ADDR);
	xauth_ldap_config.attr_addr = vmalloc(tmplen);
	if (xauth_ldap_config.attr_addr == NULL)
		goto out;
	memcpy(xauth_ldap_config.attr_addr->v, LDAP_DFLT_ADDR, tmplen);

	/* set default netmask attribute */
	tmplen = strlen(LDAP_DFLT_MASK);
	xauth_ldap_config.attr_mask = vmalloc(tmplen);
	if (xauth_ldap_config.attr_mask == NULL)
		goto out;
	memcpy(xauth_ldap_config.attr_mask->v, LDAP_DFLT_MASK, tmplen);

	/* set default group naming attribute */
	tmplen = strlen(LDAP_DFLT_GROUP);
	xauth_ldap_config.attr_group = vmalloc(tmplen);
	if (xauth_ldap_config.attr_group == NULL)
		goto out;
	memcpy(xauth_ldap_config.attr_group->v, LDAP_DFLT_GROUP, tmplen);

	/* set default member attribute */
	tmplen = strlen(LDAP_DFLT_MEMBER);
	xauth_ldap_config.attr_member = vmalloc(tmplen);
	if (xauth_ldap_config.attr_member == NULL)
		goto out;
	memcpy(xauth_ldap_config.attr_member->v, LDAP_DFLT_MEMBER, tmplen);

	error = 0;
out:
	if (error != 0)
		plog(LLV_ERROR, LOCATION, NULL, "cannot allocate memory\n");

	return error;
}

int
xauth_login_ldap(iph1, usr, pwd)
	struct ph1handle *iph1;
	char *usr;
	char *pwd;
{
	int rtn = -1;
	int res = -1;
	LDAP *ld = NULL;
	LDAPMessage *lr = NULL;
	LDAPMessage *le = NULL;
	struct berval cred;
	struct berval **bv = NULL;
	struct timeval timeout;
	char *init = NULL;
	char *filter = NULL;
	char *atlist[3];
	char *basedn = NULL;
	char *userdn = NULL;
	int tmplen = 0;
	int ecount = 0;
	int scope = LDAP_SCOPE_ONE;

	atlist[0] = NULL;
	atlist[1] = NULL;
	atlist[2] = NULL;

	/* build our initialization url */
	tmplen = strlen("ldap://:") + 17;
	tmplen += strlen(xauth_ldap_config.host->v);
	init = racoon_malloc(tmplen);
	if (init == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"unable to alloc ldap init url\n");
		goto ldap_end;
	}
	sprintf(init,"ldap://%s:%d",
		xauth_ldap_config.host->v,
		xauth_ldap_config.port );

	/* initialize the ldap handle */
	res = ldap_initialize(&ld, init);
	if (res != LDAP_SUCCESS) {
		plog(LLV_ERROR, LOCATION, NULL,
			"ldap_initialize failed: %s\n",
			ldap_err2string(res));
		goto ldap_end;
	}

	/* initialize the protocol version */
	ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
		&xauth_ldap_config.pver);

	/*
	 * attempt to bind to the ldap server.
         * default to anonymous bind unless a
	 * user dn and password has been
	 * specified in our configuration
         */
	if ((xauth_ldap_config.bind_dn != NULL)&&
	    (xauth_ldap_config.bind_pw != NULL))
	{
		cred.bv_val = xauth_ldap_config.bind_pw->v;
		cred.bv_len = strlen( cred.bv_val );
		res = ldap_sasl_bind_s(ld,
			xauth_ldap_config.bind_dn->v, NULL, &cred,
			NULL, NULL, NULL);
	}
	else
	{
		res = ldap_sasl_bind_s(ld,
			NULL, NULL, NULL,
			NULL, NULL, NULL);
	}
	
	if (res!=LDAP_SUCCESS) {
		plog(LLV_ERROR, LOCATION, NULL,
			"ldap_sasl_bind_s (search) failed: %s\n",
			ldap_err2string(res));
		goto ldap_end;
	}

	/* build an ldap user search filter */
	tmplen = strlen(xauth_ldap_config.attr_user->v);
	tmplen += 1;
	tmplen += strlen(usr);
	tmplen += 1;
	filter = racoon_malloc(tmplen);
	if (filter == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"unable to alloc ldap search filter buffer\n");
		goto ldap_end;
	}
	sprintf(filter, "%s=%s",
		xauth_ldap_config.attr_user->v, usr);

	/* build our return attribute list */
	tmplen = strlen(xauth_ldap_config.attr_addr->v) + 1;
	atlist[0] = racoon_malloc(tmplen);
	tmplen = strlen(xauth_ldap_config.attr_mask->v) + 1;
	atlist[1] = racoon_malloc(tmplen);
	if ((atlist[0] == NULL)||(atlist[1] == NULL)) {
		plog(LLV_ERROR, LOCATION, NULL,
			"unable to alloc ldap attrib list buffer\n");
		goto ldap_end;
	}
	strcpy(atlist[0],xauth_ldap_config.attr_addr->v);
	strcpy(atlist[1],xauth_ldap_config.attr_mask->v);

	/* attempt to locate the user dn */
	if (xauth_ldap_config.base != NULL)
		basedn = xauth_ldap_config.base->v;
	if (xauth_ldap_config.subtree)
		scope = LDAP_SCOPE_SUBTREE;
	timeout.tv_sec = 15;
	timeout.tv_usec = 0;
	res = ldap_search_ext_s(ld, basedn, scope,
		filter, atlist, 0, NULL, NULL,
		&timeout, 2, &lr);
	if (res != LDAP_SUCCESS) {
		plog(LLV_ERROR, LOCATION, NULL,
			"ldap_search_ext_s failed: %s\n",
			ldap_err2string(res));
		goto ldap_end;
	}

	/* check the number of ldap entries returned */
	ecount = ldap_count_entries(ld, lr);
	if (ecount < 1) {
		plog(LLV_WARNING, LOCATION, NULL, 
			"no ldap results for filter \'%s\'\n", 
			 filter);
		goto ldap_end;
	}
	if (ecount > 1) {
		plog(LLV_WARNING, LOCATION, NULL, 
			"multiple (%i) ldap results for filter \'%s\'\n", 
			ecount, filter);
	}

	/* obtain the dn from the first result */
	le = ldap_first_entry(ld, lr);
	if (le == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"ldap_first_entry failed: invalid entry returned\n");
		goto ldap_end;
	}
	userdn = ldap_get_dn(ld, le);
	if (userdn == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"ldap_get_dn failed: invalid string returned\n");
		goto ldap_end;
	}

	/* cache the user dn in the xauth state */
	iph1->mode_cfg->xauth.udn = racoon_malloc(strlen(userdn)+1);
	strcpy(iph1->mode_cfg->xauth.udn,userdn);

	/* retrieve modecfg address */
	bv = ldap_get_values_len(ld, le, xauth_ldap_config.attr_addr->v);
	if (bv != NULL)	{
		char tmpaddr[16];
		/* sanity check for address value */
		if ((bv[0]->bv_len < 7)||(bv[0]->bv_len > 15)) {
			plog(LLV_DEBUG, LOCATION, NULL,
				"ldap returned invalid modecfg address\n");
			ldap_value_free_len(bv);
			goto ldap_end;
		}
		memcpy(tmpaddr,bv[0]->bv_val,bv[0]->bv_len);
		tmpaddr[bv[0]->bv_len]=0;
		iph1->mode_cfg->addr4.s_addr = inet_addr(tmpaddr);
		iph1->mode_cfg->flags |= ISAKMP_CFG_ADDR4_EXTERN;
		plog(LLV_INFO, LOCATION, NULL,
			"ldap returned modecfg address %s\n", tmpaddr);
		ldap_value_free_len(bv);
	}

	/* retrieve modecfg netmask */
	bv = ldap_get_values_len(ld, le, xauth_ldap_config.attr_mask->v);
	if (bv != NULL)	{
		char tmpmask[16];
		/* sanity check for netmask value */
		if ((bv[0]->bv_len < 7)||(bv[0]->bv_len > 15)) {
			plog(LLV_DEBUG, LOCATION, NULL,
				"ldap returned invalid modecfg netmask\n");
			ldap_value_free_len(bv);
			goto ldap_end;
		}
		memcpy(tmpmask,bv[0]->bv_val,bv[0]->bv_len);
		tmpmask[bv[0]->bv_len]=0;
		iph1->mode_cfg->mask4.s_addr = inet_addr(tmpmask);
		iph1->mode_cfg->flags |= ISAKMP_CFG_MASK4_EXTERN;
		plog(LLV_INFO, LOCATION, NULL,
			"ldap returned modecfg netmask %s\n", tmpmask);
		ldap_value_free_len(bv);
	}

	/*
	 * finally, use the dn and the xauth
	 * password to check the users given
	 * credentials by attempting to bind
	 * to the ldap server
	 */
	plog(LLV_INFO, LOCATION, NULL,
		"attempting ldap bind for dn \'%s\'\n", userdn);
	cred.bv_val = pwd;
	cred.bv_len = strlen( cred.bv_val );
	res = ldap_sasl_bind_s(ld,
		userdn, NULL, &cred,
		NULL, NULL, NULL);
        if(res==LDAP_SUCCESS)
		rtn = 0;

ldap_end:

	/* free ldap resources */
	if (userdn != NULL)
		ldap_memfree(userdn);
	if (atlist[0] != NULL)
		racoon_free(atlist[0]);
	if (atlist[1] != NULL)
		racoon_free(atlist[1]);
	if (filter != NULL)
		racoon_free(filter);
	if (lr != NULL)
		ldap_msgfree(lr);
	if (init != NULL)
		racoon_free(init);

	ldap_unbind_ext_s(ld, NULL, NULL);

	return rtn;
}

int
xauth_group_ldap(udn, grp)
	char * udn;
	char * grp;
{
	int rtn = -1;
	int res = -1;
	LDAP *ld = NULL;
	LDAPMessage *lr = NULL;
	LDAPMessage *le = NULL;
	struct berval cred;
	struct timeval timeout;
	char *init = NULL;
	char *filter = NULL;
	char *basedn = NULL;
	char *groupdn = NULL;
	int tmplen = 0;
	int ecount = 0;
	int scope = LDAP_SCOPE_ONE;

	/* build our initialization url */
	tmplen = strlen("ldap://:") + 17;
	tmplen += strlen(xauth_ldap_config.host->v);
	init = racoon_malloc(tmplen);
	if (init == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"unable to alloc ldap init url\n");
		goto ldap_group_end;
	}
	sprintf(init,"ldap://%s:%d",
		xauth_ldap_config.host->v,
		xauth_ldap_config.port );

	/* initialize the ldap handle */
	res = ldap_initialize(&ld, init);
	if (res != LDAP_SUCCESS) {
		plog(LLV_ERROR, LOCATION, NULL,
			"ldap_initialize failed: %s\n",
			ldap_err2string(res));
		goto ldap_group_end;
	}

	/* initialize the protocol version */
	ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
		&xauth_ldap_config.pver);

	/*
	 * attempt to bind to the ldap server.
         * default to anonymous bind unless a
	 * user dn and password has been
	 * specified in our configuration
         */
	if ((xauth_ldap_config.bind_dn != NULL)&&
	    (xauth_ldap_config.bind_pw != NULL))
	{
		cred.bv_val = xauth_ldap_config.bind_pw->v;
		cred.bv_len = strlen( cred.bv_val );
		res = ldap_sasl_bind_s(ld,
			xauth_ldap_config.bind_dn->v, NULL, &cred,
			NULL, NULL, NULL);
	}
	else
	{
		res = ldap_sasl_bind_s(ld,
			NULL, NULL, NULL,
			NULL, NULL, NULL);
	}

	if (res!=LDAP_SUCCESS) {
		plog(LLV_ERROR, LOCATION, NULL,
			"ldap_sasl_bind_s (search) failed: %s\n",
			ldap_err2string(res));
		goto ldap_group_end;
	}

	/* build an ldap group search filter */
	tmplen = strlen("(&(=)(=))") + 1;
	tmplen += strlen(xauth_ldap_config.attr_group->v);
	tmplen += strlen(grp);
	tmplen += strlen(xauth_ldap_config.attr_member->v);
	tmplen += strlen(udn);
	filter = racoon_malloc(tmplen);
	if (filter == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"unable to alloc ldap search filter buffer\n");
		goto ldap_group_end;
	}
	sprintf(filter, "(&(%s=%s)(%s=%s))",
		xauth_ldap_config.attr_group->v, grp,
		xauth_ldap_config.attr_member->v, udn);

	/* attempt to locate the group dn */
	if (xauth_ldap_config.base != NULL)
		basedn = xauth_ldap_config.base->v;
	if (xauth_ldap_config.subtree)
		scope = LDAP_SCOPE_SUBTREE;
	timeout.tv_sec = 15;
	timeout.tv_usec = 0;
	res = ldap_search_ext_s(ld, basedn, scope,
		filter, NULL, 0, NULL, NULL,
		&timeout, 2, &lr);
	if (res != LDAP_SUCCESS) {
		plog(LLV_ERROR, LOCATION, NULL,
			"ldap_search_ext_s failed: %s\n",
			ldap_err2string(res));
		goto ldap_group_end;
	}

	/* check the number of ldap entries returned */
	ecount = ldap_count_entries(ld, lr);
	if (ecount < 1) {
		plog(LLV_WARNING, LOCATION, NULL, 
			"no ldap results for filter \'%s\'\n", 
			 filter);
		goto ldap_group_end;
	}

	/* success */
	rtn = 0;

	/* obtain the dn from the first result */
	le = ldap_first_entry(ld, lr);
	if (le == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"ldap_first_entry failed: invalid entry returned\n");
		goto ldap_group_end;
	}
	groupdn = ldap_get_dn(ld, le);
	if (groupdn == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"ldap_get_dn failed: invalid string returned\n");
		goto ldap_group_end;
	}

	plog(LLV_INFO, LOCATION, NULL,
		"ldap membership group returned \'%s\'\n", groupdn);
ldap_group_end:

	/* free ldap resources */
	if (groupdn != NULL)
		ldap_memfree(groupdn);
	if (filter != NULL)
		racoon_free(filter);
	if (lr != NULL)
		ldap_msgfree(lr);
	if (init != NULL)
		racoon_free(init);

	ldap_unbind_ext_s(ld, NULL, NULL);

	return rtn;
}

#endif

#ifndef ANDROID_PATCHED

int
xauth_login_system(usr, pwd)
	char *usr;
	char *pwd;
{
	struct passwd *pw;
	char *cryptpwd;
	char *syscryptpwd;
#ifdef HAVE_SHADOW_H
	struct spwd *spw;

	if ((spw = getspnam(usr)) == NULL)
		return -1;

	syscryptpwd = spw->sp_pwdp;
#endif

	if ((pw = getpwnam(usr)) == NULL)
		return -1;

#ifndef HAVE_SHADOW_H
	syscryptpwd = pw->pw_passwd;
#endif

	/* No root login. Ever. */
	if (pw->pw_uid == 0)
		return -1;

	if ((cryptpwd = crypt(pwd, syscryptpwd)) == NULL)
		return -1;

	if (strcmp(cryptpwd, syscryptpwd) == 0)
		return 0;

	return -1;
}

#endif

int
xauth_group_system(usr, grp)
	char * usr;
	char * grp;
{
	struct group * gr;
	char * member;
	int index = 0;

	gr = getgrnam(grp);
	if (gr == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"the system group name \'%s\' is unknown\n",
			grp);
		return -1;
	}

	while ((member = gr->gr_mem[index++])!=NULL) {
		if (!strcmp(member,usr)) {
			plog(LLV_INFO, LOCATION, NULL,
		                "membership validated\n");
			return 0;
		}
	}

	return -1;
}

int 
xauth_check(iph1)
	struct ph1handle *iph1;
{
	struct xauth_state *xst = &iph1->mode_cfg->xauth;

	/* 
 	 * Only the server side (edge device) really check for Xauth 
	 * status. It does it if the chose authmethod is using Xauth.
	 * On the client side (roadwarrior), we don't check anything.
	 */
	switch (AUTHMETHOD(iph1)) {
	case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R:
	case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R:
	case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R:
	/* The following are not yet implemented */
	case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R:
	case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R:
	case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_R:
	case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_R:
		if ((iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_XAUTH) == 0) {
			plog(LLV_ERROR, LOCATION, NULL,
			    "Hybrid auth negotiated but peer did not "
			    "announced as Xauth capable\n");
			return -1;
		}

		if (xst->status != XAUTHST_OK) {
			plog(LLV_ERROR, LOCATION, NULL,
			    "Hybrid auth negotiated but peer did not "
			    "succeed Xauth exchange\n");
			return -1;
		}

		return 0;
		break;
	default:
		return 0;
		break;
	}

	return 0;
}

int
group_check(iph1, grp_list, grp_count)
	struct ph1handle *iph1;
	char **grp_list;
	int grp_count;
{
	int res = -1;
	int grp_index = 0;
	char * usr = NULL;

	/* check for presence of modecfg data */

	if(iph1->mode_cfg == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"xauth group specified but modecfg not found\n");
		return res;
	}

	/* loop through our group list */

	for(; grp_index < grp_count; grp_index++) {

		/* check for presence of xauth data */

		usr = iph1->mode_cfg->xauth.authdata.generic.usr;

		if(usr == NULL) {
			plog(LLV_ERROR, LOCATION, NULL,
				"xauth group specified but xauth not found\n");
			return res;
		}

		/* call appropriate group validation funtion */

		switch (isakmp_cfg_config.groupsource) {

			case ISAKMP_CFG_GROUP_SYSTEM:
				res = xauth_group_system(
					usr,
					grp_list[grp_index]);
				break;

#ifdef HAVE_LIBLDAP
			case ISAKMP_CFG_GROUP_LDAP:
				res = xauth_group_ldap(
					iph1->mode_cfg->xauth.udn,
					grp_list[grp_index]);
				break;
#endif

			default:
				/* we should never get here */
				plog(LLV_ERROR, LOCATION, NULL,
				    "Unknown group auth source\n");
				break;
		}

		if( !res ) {
			plog(LLV_INFO, LOCATION, NULL,
				"user \"%s\" is a member of group \"%s\"\n",
				usr,
				grp_list[grp_index]);
			break;
		} else {
			plog(LLV_INFO, LOCATION, NULL,
				"user \"%s\" is not a member of group \"%s\"\n",
				usr,
				grp_list[grp_index]);
		}
	}

	return res;
}

vchar_t *
isakmp_xauth_req(iph1, attr)
	struct ph1handle *iph1;
	struct isakmp_data *attr;
{
	int type;
	size_t dlen = 0;
	int ashort = 0;
	int value = 0;
	vchar_t *buffer = NULL;
	char *mraw = NULL, *mdata;
	char *data;
	vchar_t *usr = NULL;
	vchar_t *pwd = NULL;
	size_t skip = 0;
	int freepwd = 0;

	if ((iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_XAUTH) == 0) {
		plog(LLV_ERROR, LOCATION, NULL, 
		    "Xauth mode config request but peer "
		    "did not declare itself as Xauth capable\n");
		return NULL;
	}

	type = ntohs(attr->type) & ~ISAKMP_GEN_MASK;

	/* Sanity checks */
	switch(type) {
	case XAUTH_TYPE:
		if ((ntohs(attr->type) & ISAKMP_GEN_TV) == 0) {
			plog(LLV_ERROR, LOCATION, NULL, 
			    "Unexpected long XAUTH_TYPE attribute\n");
			return NULL;
		}
		if (ntohs(attr->lorv) != XAUTH_TYPE_GENERIC) {
			plog(LLV_ERROR, LOCATION, NULL, 
			    "Unsupported Xauth authentication %d\n", 
			    ntohs(attr->lorv));
			return NULL;
		}
		ashort = 1;
		dlen = 0;
		value = XAUTH_TYPE_GENERIC;
		break;

	case XAUTH_USER_NAME:
		if (!iph1->rmconf->xauth || !iph1->rmconf->xauth->login) {
			plog(LLV_ERROR, LOCATION, NULL, "Xauth performed "
			    "with no login supplied\n");
			return NULL;
		}

		dlen = iph1->rmconf->xauth->login->l - 1;
		iph1->rmconf->xauth->state |= XAUTH_SENT_USERNAME;
		break;

#ifdef ANDROID_PATCHED
        case XAUTH_PASSCODE:
#endif  
	case XAUTH_USER_PASSWORD:
		if (!iph1->rmconf->xauth || !iph1->rmconf->xauth->login)
			return NULL;

		skip = sizeof(struct ipsecdoi_id_b);
		usr = vmalloc(iph1->rmconf->xauth->login->l - 1 + skip);
		if (usr == NULL) {
			plog(LLV_ERROR, LOCATION, NULL, 
			    "Cannot allocate memory\n");
			return NULL;
		}
		memset(usr->v, 0, skip);
		memcpy(usr->v + skip, 
		    iph1->rmconf->xauth->login->v, 
		    iph1->rmconf->xauth->login->l - 1);

		if (iph1->rmconf->xauth->pass) {
			/* A key given through racoonctl */
			pwd = iph1->rmconf->xauth->pass;
		} else {
			if ((pwd = getpskbyname(usr)) == NULL) {
				plog(LLV_ERROR, LOCATION, NULL, 
				    "No password was found for login %s\n", 
				    iph1->rmconf->xauth->login->v);
				vfree(usr);
				return NULL;
			}
			/* We have to free it before returning */
			freepwd = 1;
		}
		vfree(usr);

		iph1->rmconf->xauth->state |= XAUTH_SENT_PASSWORD;
		dlen = pwd->l;

		break;
	case XAUTH_MESSAGE:
		if ((ntohs(attr->type) & ISAKMP_GEN_TV) == 0) {
			dlen = ntohs(attr->lorv);
			if (dlen > 0) {
				mraw = (char*)(attr + 1);
				mdata = binsanitize(mraw, dlen);
				if (mdata == NULL) {
					plog(LLV_ERROR, LOCATION, iph1->remote,
					    "Cannot allocate memory\n");
					return NULL;
				}
				plog(LLV_NOTIFY,LOCATION, iph1->remote,
					"XAUTH Message: '%s'.\n",
					mdata);
				racoon_free(mdata);
			}
		}
		return NULL;
	default:
		plog(LLV_WARNING, LOCATION, NULL,
		    "Ignored attribute %s\n", s_isakmp_cfg_type(type));
		return NULL;
		break;
	}

	if ((buffer = vmalloc(sizeof(*attr) + dlen)) == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
		    "Cannot allocate memory\n");
		goto out;
	}

	attr = (struct isakmp_data *)buffer->v;
	if (ashort) {
		attr->type = htons(type | ISAKMP_GEN_TV);
		attr->lorv = htons(value);
		goto out;
	}

	attr->type = htons(type | ISAKMP_GEN_TLV);
	attr->lorv = htons(dlen);
	data = (char *)(attr + 1);

	switch(type) {
	case XAUTH_USER_NAME:
		/* 
		 * iph1->rmconf->xauth->login->v is valid, 
		 * we just checked it in the previous switch case 
		 */
		memcpy(data, iph1->rmconf->xauth->login->v, dlen);
		break;
#ifdef ANDROID_PATCHED
        case XAUTH_PASSCODE:
#endif  
	case XAUTH_USER_PASSWORD:
		memcpy(data, pwd->v, dlen);
		break;
	default:
		break;
	}

out:
	if (freepwd)
		vfree(pwd);

	return buffer;
}

vchar_t *
isakmp_xauth_set(iph1, attr)
	struct ph1handle *iph1;
	struct isakmp_data *attr;
{
	int type;
	vchar_t *buffer = NULL;
	char *data;
	struct xauth_state *xst;
	size_t dlen = 0;
	char* mraw = NULL, *mdata;

	if ((iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_XAUTH) == 0) {
		plog(LLV_ERROR, LOCATION, NULL, 
		    "Xauth mode config set but peer "
		    "did not declare itself as Xauth capable\n");
		return NULL;
	}

	type = ntohs(attr->type) & ~ISAKMP_GEN_MASK;

	switch(type) {
	case XAUTH_STATUS:
		/* 
		 * We should only receive ISAKMP mode_cfg SET XAUTH_STATUS
		 * when running as a client (initiator).
		 */
		xst = &iph1->mode_cfg->xauth;
		switch(AUTHMETHOD(iph1)) {
		case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I:
		case FICTIVE_AUTH_METHOD_XAUTH_PSKEY_I:
		case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I:
		/* Not implemented ... */
		case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I:
		case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I:
		case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_I:
		case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_I:
			break;
		default:
			plog(LLV_ERROR, LOCATION, NULL, 
			    "Unexpected XAUTH_STATUS_OK\n");
			return NULL;
			break;
		}

		/* If we got a failure, delete iph1 */
		if (ntohs(attr->lorv) != XAUTH_STATUS_OK) {
			plog(LLV_ERROR, LOCATION, NULL, 
			    "Xauth authentication failed\n");

			EVT_PUSH(iph1->local, iph1->remote, 
			    EVTT_XAUTH_FAILED, NULL);

			iph1->mode_cfg->flags |= ISAKMP_CFG_DELETE_PH1;
		} else {
			EVT_PUSH(iph1->local, iph1->remote, 
			    EVTT_XAUTH_SUCCESS, NULL);
		}


		/* We acknowledge it */
		break;
	case XAUTH_MESSAGE:
		if ((ntohs(attr->type) & ISAKMP_GEN_TV) == 0) {
			dlen = ntohs(attr->lorv);
			if (dlen > 0) {
				mraw = (char*)(attr + 1);
				mdata = binsanitize(mraw, dlen);
				if (mdata == NULL) {
					plog(LLV_ERROR, LOCATION, iph1->remote,
					    "Cannot allocate memory\n");
					return NULL;
				}
				plog(LLV_NOTIFY,LOCATION, iph1->remote,
					"XAUTH Message: '%s'.\n",
					mdata);
				racoon_free(mdata);
			}
		}

	default:
		plog(LLV_WARNING, LOCATION, NULL,
		    "Ignored attribute %s\n", s_isakmp_cfg_type(type));
		return NULL;
		break;
	}

	if ((buffer = vmalloc(sizeof(*attr))) == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
		    "Cannot allocate memory\n");
		return NULL;
	}

	attr = (struct isakmp_data *)buffer->v;
	attr->type = htons(type | ISAKMP_GEN_TV);
	attr->lorv = htons(0);

	return buffer;
}


void 
xauth_rmstate(xst)
	struct xauth_state *xst;
{
	switch (xst->authtype) {
	case XAUTH_TYPE_GENERIC:
		if (xst->authdata.generic.usr)
			racoon_free(xst->authdata.generic.usr);

		if (xst->authdata.generic.pwd)
			racoon_free(xst->authdata.generic.pwd);

		break;

	case XAUTH_TYPE_CHAP:
	case XAUTH_TYPE_OTP:
	case XAUTH_TYPE_SKEY:
		plog(LLV_WARNING, LOCATION, NULL, 
		    "Unsupported authtype %d\n", xst->authtype);
		break;

	default:
		plog(LLV_WARNING, LOCATION, NULL, 
		    "Unexpected authtype %d\n", xst->authtype);
		break;
	}

#ifdef HAVE_LIBLDAP
	if (xst->udn != NULL)
		racoon_free(xst->udn);
#endif
	return;
}

int
xauth_rmconf_used(xauth_rmconf)
	struct xauth_rmconf **xauth_rmconf;
{
	if (*xauth_rmconf == NULL) {
		*xauth_rmconf = racoon_malloc(sizeof(**xauth_rmconf));
		if (*xauth_rmconf == NULL) {
			plog(LLV_ERROR, LOCATION, NULL, 
			    "xauth_rmconf_used: malloc failed\n");
			return -1;
		}

		(*xauth_rmconf)->login = NULL;
		(*xauth_rmconf)->pass = NULL;
		(*xauth_rmconf)->state = 0;
	}

	return 0;
}

void 
xauth_rmconf_delete(xauth_rmconf)
	struct xauth_rmconf **xauth_rmconf;
{
	if (*xauth_rmconf != NULL) { 
		if ((*xauth_rmconf)->login != NULL)
			vfree((*xauth_rmconf)->login);
		if ((*xauth_rmconf)->pass != NULL)
			vfree((*xauth_rmconf)->pass);

		racoon_free(*xauth_rmconf);
		*xauth_rmconf = NULL;
	}

	return;
}