root/crypto/dist/ipsec-tools/src/racoon/ipsec_doi.c
/*      $NetBSD: ipsec_doi.c,v 1.54 2025/03/08 16:39:08 christos Exp $  */

/* Id: ipsec_doi.c,v 1.55 2006/08/17 09:20:41 vanhu Exp */

/*
 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
 * 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 <netinet/in.h>

#include PATH_IPSEC_H

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <netdb.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 "var.h"
#include "vmbuf.h"
#include "misc.h"
#include "plog.h"
#include "debug.h"

#include "cfparse_proto.h"
#include "isakmp_var.h"
#include "isakmp.h"
#include "ipsec_doi.h"
#include "oakley.h"
#include "remoteconf.h"
#include "localconf.h"
#include "sockmisc.h"
#include "handler.h"
#include "policy.h"
#include "algorithm.h"
#include "sainfo.h"
#include "proposal.h"
#include "crypto_openssl.h"
#include "strnames.h"
#include "gcmalloc.h"

#ifdef ENABLE_NATT
#include "nattraversal.h"
#endif

#ifdef HAVE_GSSAPI
#include <iconv.h>
#include "gssapi.h"
#ifdef HAVE_ICONV_2ND_CONST
#define __iconv_const const
#else
#define __iconv_const
#endif
#endif

static vchar_t *get_ph1approval(struct ph1handle *, uint32_t, uint32_t,
    struct prop_pair **);
static int get_ph1approvalx(struct remoteconf *, void *);

static int t2isakmpsa(struct isakmp_pl_t *, struct isakmpsa *, uint32_t);
static int cmp_aproppair_i(struct prop_pair *, struct prop_pair *);
static struct prop_pair *get_ph2approval(struct ph2handle *,
    struct prop_pair **);
static struct prop_pair *get_ph2approvalx(struct ph2handle *,
    struct prop_pair *);
static void free_proppair0(struct prop_pair *);
static struct prop_pair ** get_proppair_and_doi_sit(vchar_t *, int,
    uint32_t *, uint32_t *);

static int get_transform(struct isakmp_pl_p *, struct prop_pair **, int *);
static uint32_t ipsecdoi_set_ld(vchar_t *);

static int check_doi(uint32_t);
static int check_situation(uint32_t);

static int check_prot_main(int);
static int check_prot_quick(int);
static int (*check_protocol[])(int) = {
        check_prot_main,        /* IPSECDOI_TYPE_PH1 */
        check_prot_quick,       /* IPSECDOI_TYPE_PH2 */
};

static int check_spi_size(int, int);

static int check_trns_isakmp(int);
static int check_trns_ah(int);
static int check_trns_esp(int);
static int check_trns_ipcomp(int);
static int (*check_transform[])(int) = {
        0,
        check_trns_isakmp,      /* IPSECDOI_PROTO_ISAKMP */
        check_trns_ah,          /* IPSECDOI_PROTO_IPSEC_AH */
        check_trns_esp,         /* IPSECDOI_PROTO_IPSEC_ESP */
        check_trns_ipcomp,      /* IPSECDOI_PROTO_IPCOMP */
};

static int check_attr_isakmp(struct isakmp_pl_t *);
static int check_attr_ah(struct isakmp_pl_t *);
static int check_attr_esp(struct isakmp_pl_t *);
static int check_attr_ipsec(int, struct isakmp_pl_t *);
static int check_attr_ipcomp(struct isakmp_pl_t *);
static int (*check_attributes[])(struct isakmp_pl_t *) = {
        0,
        check_attr_isakmp,      /* IPSECDOI_PROTO_ISAKMP */
        check_attr_ah,          /* IPSECDOI_PROTO_IPSEC_AH */
        check_attr_esp,         /* IPSECDOI_PROTO_IPSEC_ESP */
        check_attr_ipcomp,      /* IPSECDOI_PROTO_IPCOMP */
};

static int setph1prop(struct isakmpsa *, caddr_t);
static int setph1trns(struct isakmpsa *, caddr_t);
static int setph1attr(struct isakmpsa *, caddr_t);
static vchar_t *setph2proposal0(const struct ph2handle *,
    const struct saprop *, const struct saproto *);

struct ph1approvalx_ctx {
        struct prop_pair *p;
        struct isakmpsa *sa;
};

/*%%%*/
/*
 * check phase 1 SA payload.
 * make new SA payload to be replyed not including general header.
 * the pointer to one of isakmpsa in proposal is set into iph1->approval.
 * OUT:
 *      positive: the pointer to new buffer of SA payload.
 *                network byte order.
 *      NULL    : error occurd.
 */
int
ipsecdoi_checkph1proposal(vchar_t *sa, struct ph1handle *iph1)
{
        vchar_t *newsa;         /* new SA payload approved. */
        struct prop_pair **pair;
        uint32_t doitype, sittype;

        /* get proposal pair */
        pair = get_proppair_and_doi_sit(sa, IPSECDOI_TYPE_PH1,
                                        &doitype, &sittype);
        if (pair == NULL)
                return -1;

        /* check and get one SA for use */
        newsa = get_ph1approval(iph1, doitype, sittype, pair);
        free_proppair(pair);

        if (newsa == NULL)
                return -1;

        iph1->sa_ret = newsa;
        return 0;
}

static void
print_ph1proposal(struct prop_pair *pair, struct isakmpsa *s)
{
        struct isakmp_pl_p *prop = pair->prop;
        struct isakmp_pl_t *trns = pair->trns;

        plog(LLV_DEBUG, LOCATION, NULL,
             "prop#=%d, prot-id=%s, spi-size=%d, #trns=%d\n",
             prop->p_no, s_ipsecdoi_proto(prop->proto_id),
             prop->spi_size, prop->num_t);
        plog(LLV_DEBUG, LOCATION, NULL,
             "trns#=%d, trns-id=%s\n",
             trns->t_no, s_ipsecdoi_trns(prop->proto_id, trns->t_id));
        plog(LLV_DEBUG, LOCATION, NULL,
             "  lifetime = %ld\n", (long) s->lifetime);
        plog(LLV_DEBUG, LOCATION, NULL,
             "  lifebyte = %zu\n", s->lifebyte);
        plog(LLV_DEBUG, LOCATION, NULL,
             "  enctype = %s\n",
             s_oakley_attr_v(OAKLEY_ATTR_ENC_ALG, s->enctype));
        plog(LLV_DEBUG, LOCATION, NULL,
             "  encklen = %d\n", s->encklen);
        plog(LLV_DEBUG, LOCATION, NULL,
             "  hashtype = %s\n",
             s_oakley_attr_v(OAKLEY_ATTR_HASH_ALG, s->hashtype));
        plog(LLV_DEBUG, LOCATION, NULL,
             "  authmethod = %s\n",
             s_oakley_attr_v(OAKLEY_ATTR_AUTH_METHOD, s->authmethod));
        plog(LLV_DEBUG, LOCATION, NULL,
             "  dh_group = %s\n",
             s_oakley_attr_v(OAKLEY_ATTR_GRP_DESC, s->dh_group));
}


/*
 * acceptable check for remote configuration.
 * return a new SA payload to be reply to peer.
 */

static vchar_t *
get_ph1approval(struct ph1handle *iph1, uint32_t doitype, uint32_t sittype,
    struct prop_pair **pair)
{
        vchar_t *newsa;
        struct ph1approvalx_ctx ctx;
        struct prop_pair *s, *p;
        struct rmconfselector rmsel;
        struct isakmpsa *sa;
        int i;

        memset(&rmsel, 0, sizeof(rmsel));
        rmsel.remote = iph1->remote;

        if (iph1->approval) {
                delisakmpsa(iph1->approval);
                iph1->approval = NULL;
        }

        for (i = 0; i < MAXPROPPAIRLEN; i++) {
                if (pair[i] == NULL)
                        continue;
                for (s = pair[i]; s; s = s->next) {
                        /* compare proposal and select one */
                        for (p = s; p; p = p->tnext) {
                                sa = newisakmpsa();
                                ctx.p = p;
                                ctx.sa = sa;
                                if (t2isakmpsa(p->trns, sa,
                                               iph1->vendorid_mask) < 0)
                                        continue;
                                print_ph1proposal(p, sa);
                                if (iph1->rmconf != NULL) {
                                        if (get_ph1approvalx(iph1->rmconf, &ctx))
                                                goto found;
                                } else {
                                        if (enumrmconf(&rmsel, get_ph1approvalx, &ctx))
                                                goto found;
                                }
                                delisakmpsa(sa);
                        }
                }
        }

        plog(LLV_ERROR, LOCATION, NULL, "no suitable proposal found.\n");

        return NULL;

found:
        sa = ctx.sa;
        plog(LLV_DEBUG, LOCATION, NULL, "an acceptable proposal found.\n");

        /* check DH group settings */
        if (sa->dhgrp) {
                if (sa->dhgrp->prime && sa->dhgrp->gen1) {
                        /* it's ok */
                        goto saok;
                }
                plog(LLV_WARNING, LOCATION, NULL,
                        "invalid DH parameter found, use default.\n");
                oakley_dhgrp_free(sa->dhgrp);
                sa->dhgrp=NULL;
        }

        if (oakley_setdhgroup(sa->dh_group, &sa->dhgrp) == -1) {
                sa->dhgrp = NULL;
                delisakmpsa(sa);
                return NULL;
        }

saok:
#ifdef HAVE_GSSAPI
        if (sa->gssid != NULL)
                plog(LLV_DEBUG, LOCATION, NULL, "gss id in new sa '%.*s'\n",
                    (int)sa->gssid->l, sa->gssid->v);
        if (iph1->side == INITIATOR) {
                if (iph1->rmconf->proposal->gssid != NULL)
                        iph1->gi_i = vdup(iph1->rmconf->proposal->gssid);
                if (sa->gssid != NULL)
                        iph1->gi_r = vdup(sa->gssid);
        } else {
                if (sa->gssid != NULL) {
                        iph1->gi_r = vdup(sa->gssid);
                        iph1->gi_i = gssapi_get_id(iph1);
                }
        }
        if (iph1->gi_i != NULL)
                plog(LLV_DEBUG, LOCATION, NULL, "GIi is %.*s\n",
                    (int)iph1->gi_i->l, iph1->gi_i->v);
        if (iph1->gi_r != NULL)
                plog(LLV_DEBUG, LOCATION, NULL, "GIr is %.*s\n",
                    (int)iph1->gi_r->l, iph1->gi_r->v);
#endif
        plog(LLV_DEBUG, LOCATION, NULL, "agreed on %s auth.\n",
             s_oakley_attr_method(sa->authmethod));

        newsa = get_sabyproppair(doitype, sittype, p);
        if (newsa == NULL)
                delisakmpsa(sa);
        else
                iph1->approval = sa;

        return newsa;
}

/*
 * compare peer's single proposal and all of my proposal.
 * and select one if suiatable.
 */
static int
get_ph1approvalx(struct remoteconf *rmconf, void *ctx)
{
        struct ph1approvalx_ctx *pctx = (struct ph1approvalx_ctx *) ctx;
        struct isakmpsa *sa;

        /* do the hard work */
        sa = checkisakmpsa(rmconf->pcheck_level, pctx->sa, rmconf->proposal);
        if (sa == NULL)
                return 0;

        /* duplicate and modify the found SA to match proposal */
        sa = dupisakmpsa(sa);

        switch (rmconf->pcheck_level) {
        case PROP_CHECK_OBEY:
                sa->lifetime = pctx->sa->lifetime;
                sa->lifebyte = pctx->sa->lifebyte;
                sa->encklen = pctx->sa->encklen;
                break;
        case PROP_CHECK_CLAIM:
        case PROP_CHECK_STRICT:
                if (pctx->sa->lifetime < sa->lifetime)
                        sa->lifetime = pctx->sa->lifetime;
                if (pctx->sa->lifebyte < sa->lifebyte)
                        sa->lifebyte = pctx->sa->lifebyte;
                if (pctx->sa->encklen > sa->encklen)
                        sa->encklen = pctx->sa->encklen;
                break;
        default:
                break;
        }

        /* replace the proposal with our approval sa */
        delisakmpsa(pctx->sa);
        pctx->sa = sa;

        return 1;
}

/*
 * get ISAKMP data attributes
 */
static int
t2isakmpsa(struct isakmp_pl_t *trns, struct isakmpsa *sa,
    uint32_t vendorid_mask)
{
        struct isakmp_data *d, *prev;
        int flag, type;
        int error = -1;
        int life_t;
        int keylen = 0;
        vchar_t *val = NULL;
        int len, tlen;
        u_char *p;

        tlen = ntohs(trns->h.len) - sizeof(*trns);
        prev = (struct isakmp_data *)NULL;
        d = (struct isakmp_data *)(trns + 1);

        /* default */
        life_t = OAKLEY_ATTR_SA_LD_TYPE_DEFAULT;
        sa->lifetime = OAKLEY_ATTR_SA_LD_SEC_DEFAULT;
        sa->lifebyte = 0;
        sa->dhgrp = racoon_calloc(1, sizeof(struct dhgroup));
        if (!sa->dhgrp)
                goto err;

        while (tlen > 0) {

                type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
                flag = ntohs(d->type) & ISAKMP_GEN_MASK;

                plog(LLV_DEBUG, LOCATION, NULL,
                        "type=%s, flag=0x%04x, lorv=%s\n",
                        s_oakley_attr(type), flag,
                        s_oakley_attr_v(type, ntohs(d->lorv)));

                /* get variable-sized item */
                switch (type) {
                case OAKLEY_ATTR_GRP_PI:
                case OAKLEY_ATTR_GRP_GEN_ONE:
                case OAKLEY_ATTR_GRP_GEN_TWO:
                case OAKLEY_ATTR_GRP_CURVE_A:
                case OAKLEY_ATTR_GRP_CURVE_B:
                case OAKLEY_ATTR_SA_LD:
                case OAKLEY_ATTR_GRP_ORDER:
                        if (flag) {     /*TV*/
                                len = 2;
                                p = (u_char *)&d->lorv;
                        } else {        /*TLV*/
                                len = ntohs(d->lorv);
                                p = (u_char *)(d + 1);
                        }
                        val = vmalloc(len);
                        if (!val)
                                return -1;
                        memcpy(val->v, p, len);
                        break;

                default:
                        break;
                }

                switch (type) {
                case OAKLEY_ATTR_ENC_ALG:
                        sa->enctype = (uint16_t)ntohs(d->lorv);
                        break;

                case OAKLEY_ATTR_HASH_ALG:
                        sa->hashtype = (uint16_t)ntohs(d->lorv);
                        break;

                case OAKLEY_ATTR_AUTH_METHOD:
                        sa->authmethod = ntohs(d->lorv);
#ifdef HAVE_GSSAPI
                        if (sa->authmethod == OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB_REAL &&
                            (vendorid_mask & VENDORID_GSSAPI_MASK))
                                sa->authmethod = OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB;
#endif
                        break;

                case OAKLEY_ATTR_GRP_DESC:
                        sa->dh_group = (uint16_t)ntohs(d->lorv);
                        break;

                case OAKLEY_ATTR_GRP_TYPE:
                {
                        int xtype = (int)ntohs(d->lorv);
                        if (xtype == OAKLEY_ATTR_GRP_TYPE_MODP)
                                sa->dhgrp->type = xtype;
                        else
                                return -1;
                        break;
                }
                case OAKLEY_ATTR_GRP_PI:
                        sa->dhgrp->prime = val;
                        break;

                case OAKLEY_ATTR_GRP_GEN_ONE:
                        vfree(val);
                        if (!flag)
                                sa->dhgrp->gen1 = ntohs(d->lorv);
                        else {
                                int xlen = ntohs(d->lorv);
                                sa->dhgrp->gen1 = 0;
                                if (xlen > 4)
                                        return -1;
                                memcpy(&sa->dhgrp->gen1, d + 1, xlen);
                                sa->dhgrp->gen1 = ntohl(sa->dhgrp->gen1);
                        }
                        break;

                case OAKLEY_ATTR_GRP_GEN_TWO:
                        vfree(val);
                        if (!flag)
                                sa->dhgrp->gen2 = ntohs(d->lorv);
                        else {
                                int xlen = ntohs(d->lorv);
                                sa->dhgrp->gen2 = 0;
                                if (xlen > 4)
                                        return -1;
                                memcpy(&sa->dhgrp->gen2, d + 1, xlen);
                                sa->dhgrp->gen2 = ntohl(sa->dhgrp->gen2);
                        }
                        break;

                case OAKLEY_ATTR_GRP_CURVE_A:
                        sa->dhgrp->curve_a = val;
                        break;

                case OAKLEY_ATTR_GRP_CURVE_B:
                        sa->dhgrp->curve_b = val;
                        break;

                case OAKLEY_ATTR_SA_LD_TYPE:
                {
                        int xtype = (int)ntohs(d->lorv);
                        switch (xtype) {
                        case OAKLEY_ATTR_SA_LD_TYPE_SEC:
                        case OAKLEY_ATTR_SA_LD_TYPE_KB:
                                life_t = xtype;
                                break;
                        default:
                                life_t = OAKLEY_ATTR_SA_LD_TYPE_DEFAULT;
                                break;
                        }
                        break;
                }
                case OAKLEY_ATTR_SA_LD:
                        if (!prev
                         || (ntohs(prev->type) & ~ISAKMP_GEN_MASK) !=
                                        OAKLEY_ATTR_SA_LD_TYPE) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                    "life duration must follow ltype\n");
                                break;
                        }

                        switch (life_t) {
                        case IPSECDOI_ATTR_SA_LD_TYPE_SEC:
                                sa->lifetime = ipsecdoi_set_ld(val);
                                vfree(val);
                                if (sa->lifetime == 0) {
                                        plog(LLV_ERROR, LOCATION, NULL,
                                                "invalid life duration.\n");
                                        goto err;
                                }
                                break;
                        case IPSECDOI_ATTR_SA_LD_TYPE_KB:
                                sa->lifebyte = ipsecdoi_set_ld(val);
                                vfree(val);
                                if (sa->lifebyte == 0) {
                                        plog(LLV_ERROR, LOCATION, NULL,
                                                "invalid life duration.\n");
                                        goto err;
                                }
                                break;
                        default:
                                vfree(val);
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "invalid life type: %d\n", life_t);
                                goto err;
                        }
                        break;

                case OAKLEY_ATTR_KEY_LEN:
                {
                        int xlen = ntohs(d->lorv);
                        if (xlen % 8 != 0) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "keylen %d: not multiple of 8\n",
                                        xlen);
                                goto err;
                        }
                        sa->encklen = (uint16_t)xlen;
                        keylen++;
                        break;
                }
                case OAKLEY_ATTR_PRF:
                case OAKLEY_ATTR_FIELD_SIZE:
                        /* unsupported */
                        break;

                case OAKLEY_ATTR_GRP_ORDER:
                        sa->dhgrp->order = val;
                        break;
#ifdef HAVE_GSSAPI
                case OAKLEY_ATTR_GSS_ID:
                {
                        int xerror = -1;
                        iconv_t cd = (iconv_t) -1;
                        size_t srcleft, dstleft, rv;
                        __iconv_const char *src;
                        char *dst;
                        int xlen = ntohs(d->lorv);

                        /*
                         * Older verions of racoon just placed the
                         * ISO-Latin-1 string on the wire directly.
                         * Check to see if we are configured to be
                         * compatible with this behavior.
                         */
                        if (lcconf->gss_id_enc == LC_GSSENC_LATIN1) {
                                if ((sa->gssid = vmalloc(xlen)) == NULL) {
                                        plog(LLV_ERROR, LOCATION, NULL,
                                            "failed to allocate memory\n");
                                        goto out;
                                }
                                memcpy(sa->gssid->v, d + 1, xlen);
                                plog(LLV_DEBUG, LOCATION, NULL,
                                    "received old-style gss "
                                    "id '%.*s' (len %zu)\n",
                                    (int)sa->gssid->l, sa->gssid->v,
                                    sa->gssid->l);
                                xerror = 0;
                                goto out;
                        }

                        /*
                         * For Windows 2000 compatibility, we expect
                         * the GSS ID attribute on the wire to be
                         * encoded in UTF-16LE.  Internally, we work
                         * in ISO-Latin-1.  Therefore, we should need
                         * 1/2 the specified length, which should always
                         * be a multiple of 2 octets.
                         */
                        cd = iconv_open("latin1", "utf-16le");
                        if (cd == (iconv_t) -1) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                    "unable to initialize utf-16le -> latin1 "
                                    "conversion descriptor: %s\n",
                                    strerror(errno));
                                goto out;
                        }

                        if ((sa->gssid = vmalloc(xlen / 2)) == NULL) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                    "failed to allocate memory\n");
                                goto out;
                        }

                        src = (__iconv_const char *)(d + 1);
                        srcleft = xlen;

                        dst = sa->gssid->v;
                        dstleft = xlen / 2;

                        rv = iconv(cd, (__iconv_const char **)&src, &srcleft,
                                   &dst, &dstleft);
                        if (rv != 0) {
                                if (rv == (size_t)-1) {
                                        plog(LLV_ERROR, LOCATION, NULL,
                                            "unable to convert GSS ID from "
                                            "utf-16le -> latin1: %s\n",
                                            strerror(errno));
                                } else {
                                        plog(LLV_ERROR, LOCATION, NULL,
                                            "%zd character%s in GSS ID cannot "
                                            "be represented in latin1\n",
                                            rv, rv == 1 ? "" : "s");
                                }
                                goto out;
                        }

                        /* XXX dstleft should always be 0; assert it? */
                        sa->gssid->l = (xlen / 2) - dstleft;

                        plog(LLV_DEBUG, LOCATION, NULL,
                            "received gss id '%.*s' (len %zu)\n",
                            (int)sa->gssid->l, sa->gssid->v, sa->gssid->l);

                        xerror = 0;
out:
                        if (cd != (iconv_t)-1)
                                (void)iconv_close(cd);

                        if ((xerror != 0) && (sa->gssid != NULL)) {
                                vfree(sa->gssid);
                                sa->gssid = NULL;
                        }
                        break;
                }
#endif /* HAVE_GSSAPI */

                default:
                        break;
                }

                prev = d;
                if (flag) {
                        tlen -= sizeof(*d);
                        d = (struct isakmp_data *)((char *)d + sizeof(*d));
                } else {
                        tlen -= (sizeof(*d) + ntohs(d->lorv));
                        d = (struct isakmp_data *)((char *)d + sizeof(*d) + ntohs(d->lorv));
                }
        }

        /* key length must not be specified on some algorithms */
        if (keylen) {
                if (sa->enctype == OAKLEY_ATTR_ENC_ALG_DES
#ifdef HAVE_OPENSSL_IDEA_H
                 || sa->enctype == OAKLEY_ATTR_ENC_ALG_IDEA
#endif
                 || sa->enctype == OAKLEY_ATTR_ENC_ALG_3DES) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "keylen must not be specified "
                                "for encryption algorithm %d\n",
                                sa->enctype);
                        return -1;
                }
        }

        return 0;
err:
        return error;
}

/*%%%*/
/*
 * check phase 2 SA payload and select single proposal.
 * make new SA payload to be replyed not including general header.
 * This function is called by responder only.
 * OUT:
 *      0: succeed.
 *      -1: error occured.
 */
int
ipsecdoi_selectph2proposal(struct ph2handle *iph2)
{
        struct prop_pair **pair;
        struct prop_pair *ret;
        uint32_t doitype, sittype;

        /* get proposal pair */
        pair = get_proppair_and_doi_sit(iph2->sa, IPSECDOI_TYPE_PH2,
                                        &doitype, &sittype);
        if (pair == NULL)
                return -1;

        /* check and select a proposal. */
        ret = get_ph2approval(iph2, pair);
        free_proppair(pair);
        if (ret == NULL)
                return -1;

        /* make a SA to be replayed. */
        /* SPI must be updated later. */
        iph2->sa_ret = get_sabyproppair(doitype, sittype, ret);
        free_proppair0(ret);
        if (iph2->sa_ret == NULL)
                return -1;

        return 0;
}

/*
 * check phase 2 SA payload returned from responder.
 * This function is called by initiator only.
 * OUT:
 *      0: valid.
 *      -1: invalid.
 */
int
ipsecdoi_checkph2proposal(struct ph2handle *iph2)
{
        struct prop_pair **rpair = NULL, **spair = NULL;
        struct prop_pair *p;
        int i, n, num;
        int error = -1;
        vchar_t *sa_ret = NULL;
        uint32_t doitype, sittype;

        /* get proposal pair of SA sent. */
        spair = get_proppair_and_doi_sit(iph2->sa, IPSECDOI_TYPE_PH2,
                                         &doitype, &sittype);
        if (spair == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get prop pair.\n");
                goto end;
        }

        /* XXX should check the number of transform */

        /* get proposal pair of SA replayed */
        rpair = get_proppair(iph2->sa_ret, IPSECDOI_TYPE_PH2);
        if (rpair == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get prop pair.\n");
                goto end;
        }

        /* check proposal is only one ? */
        n = 0;
        num = 0;
        for (i = 0; i < MAXPROPPAIRLEN; i++) {
                if (rpair[i]) {
                        n = i;
                        num++;
                }
        }
        if (num == 0) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "no proposal received.\n");
                goto end;
        }
        if (num != 1) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "some proposals received.\n");
                goto end;
        }

        if (spair[n] == NULL) {
                plog(LLV_WARNING, LOCATION, NULL,
                        "invalid proposal number:%d received.\n", i);
        }


        if (rpair[n]->tnext != NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "multi transforms replyed.\n");
                goto end;
        }

        if (cmp_aproppair_i(rpair[n], spair[n])) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "proposal mismathed.\n");
                goto end;
        }

        /*
         * check and select a proposal.
         * ensure that there is no modification of the proposal by
         * cmp_aproppair_i()
         */
        p = get_ph2approval(iph2, rpair);
        if (p == NULL)
                goto end;

        /* make a SA to be replayed. */
        sa_ret = iph2->sa_ret;
        iph2->sa_ret = get_sabyproppair(doitype, sittype, p);
        free_proppair0(p);
        if (iph2->sa_ret == NULL)
                goto end;

        error = 0;

end:
        if (rpair)
                free_proppair(rpair);
        if (spair)
                free_proppair(spair);
        if (sa_ret)
                vfree(sa_ret);

        return error;
}

/*
 * compare two prop_pair which is assumed to have same proposal number.
 * the case of bundle or single SA, NOT multi transforms.
 * a: a proposal that is multi protocols and single transform, usually replyed.
 * b: a proposal that is multi protocols and multi transform, usually sent.
 * NOTE: this function is for initiator.
 * OUT
 *      0: equal
 *      1: not equal
 * XXX cannot understand the comment!
 */
static int
cmp_aproppair_i(struct prop_pair *a, struct prop_pair *b)
{
        struct prop_pair *p, *q, *r;
        int len;

        for (p = a, q = b; p && q; p = p->next, q = q->next) {
                for (r = q; r; r = r->tnext) {
                        /* compare trns */
                        if (p->trns->t_no == r->trns->t_no)
                                break;
                }
                if (!r) {
                        /* no suitable transform found */
                        plog(LLV_ERROR, LOCATION, NULL,
                                "no suitable transform found.\n");
                        return -1;
                }

                /* compare prop */
                if (p->prop->p_no != r->prop->p_no) {
                        plog(LLV_WARNING, LOCATION, NULL,
                                "proposal #%d mismatched, "
                                "expected #%d.\n",
                                r->prop->p_no, p->prop->p_no);
                        /*FALLTHROUGH*/
                }

                if (p->prop->proto_id != r->prop->proto_id) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "proto_id mismathed: my:%d peer:%d\n",
                                r->prop->proto_id, p->prop->proto_id);
                        return -1;
                }

                if (p->prop->spi_size != r->prop->spi_size) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid spi size: %d.\n",
                                p->prop->spi_size);
                        return -1;
                }

                /* check #of transforms */
                if (p->prop->num_t != 1) {
                        plog(LLV_WARNING, LOCATION, NULL,
                                "#of transform is %d, "
                                "but expected 1.\n", p->prop->num_t);
                        /*FALLTHROUGH*/
                }

                if (p->trns->t_id != r->trns->t_id) {
                        plog(LLV_WARNING, LOCATION, NULL,
                                "transform number has been modified.\n");
                        /*FALLTHROUGH*/
                }
                if (p->trns->reserved != r->trns->reserved) {
                        plog(LLV_WARNING, LOCATION, NULL,
                                "reserved field should be zero.\n");
                        /*FALLTHROUGH*/
                }

                /* compare attribute */
                len = ntohs(r->trns->h.len) - sizeof(*p->trns);
                if (memcmp(p->trns + 1, r->trns + 1, len) != 0) {
                        plog(LLV_WARNING, LOCATION, NULL,
                                "attribute has been modified.\n");
                        /*FALLTHROUGH*/
                }
        }
        if ((p && !q) || (!p && q)) {
                /* # of protocols mismatched */
                plog(LLV_ERROR, LOCATION, NULL,
                        "#of protocols mismatched.\n");
                return -1;
        }

        return 0;
}

/*
 * acceptable check for policy configuration.
 * return a new SA payload to be reply to peer.
 */
static struct prop_pair *
get_ph2approval(struct ph2handle *iph2, struct prop_pair **pair)
{
        struct prop_pair *ret;
        int i;

        iph2->approval = NULL;

        plog(LLV_DEBUG, LOCATION, NULL,
                "begin compare proposals.\n");

        for (i = 0; i < MAXPROPPAIRLEN; i++) {
                if (pair[i] == NULL)
                        continue;
                plog(LLV_DEBUG, LOCATION, NULL,
                        "pair[%d]: %p\n", i, pair[i]);
                print_proppair(LLV_DEBUG, pair[i]);

                /* compare proposal and select one */
                ret = get_ph2approvalx(iph2, pair[i]);
                if (ret != NULL) {
                        /* found */
                        return ret;
                }
        }

        plog(LLV_ERROR, LOCATION, NULL, "no suitable policy found.\n");

        return NULL;
}

/*
 * compare my proposal and peers just one proposal.
 * set a approval.
 */
static struct prop_pair *
get_ph2approvalx(struct ph2handle *iph2, struct prop_pair *pp)
{
        struct prop_pair *ret = NULL;
        struct saprop *pr0, *pr = NULL;
        struct saprop *q1, *q2;

        pr0 = aproppair2saprop(pp);
        if (pr0 == NULL)
                return NULL;

        for (q1 = pr0; q1; q1 = q1->next) {
                for (q2 = iph2->proposal; q2; q2 = q2->next) {
                        plog(LLV_DEBUG, LOCATION, NULL,
                                "peer's single bundle:\n");
                        printsaprop0(LLV_DEBUG, q1);
                        plog(LLV_DEBUG, LOCATION, NULL,
                                "my single bundle:\n");
                        printsaprop0(LLV_DEBUG, q2);

                        pr = cmpsaprop_alloc(iph2->ph1, q1, q2, iph2->side);
                        if (pr != NULL)
                                goto found;

                        plog(LLV_ERROR, LOCATION, NULL,
                                "not matched\n");
                }
        }
        /* no proposal matching */
err:
        flushsaprop(pr0);
        return NULL;

found:
        flushsaprop(pr0);
        plog(LLV_DEBUG, LOCATION, NULL, "matched\n");
        iph2->approval = pr;

    {
        struct saproto *sp;
        struct prop_pair *p, *x;
        struct prop_pair *n = NULL;

        ret = NULL;

        for (p = pp; p; p = p->next) {
                /*
                 * find a proposal with matching proto_id.
                 * we have analyzed validity already, in cmpsaprop_alloc().
                 */
                for (sp = pr->head; sp; sp = sp->next) {
                        if (sp->proto_id == p->prop->proto_id)
                                break;
                }
                if (!sp)
                        goto err;
                if (sp->head->next)
                        goto err;       /* XXX */

                for (x = p; x; x = x->tnext)
                        if (sp->head->trns_no == x->trns->t_no)
                                break;
                if (!x)
                        goto err;       /* XXX */

                n = racoon_calloc(1, sizeof(struct prop_pair));
                if (n == NULL) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "failed to get buffer.\n");
                        goto err;
                }

                n->prop = x->prop;
                n->trns = x->trns;

                /* need to preserve the order */
                for (x = ret; x && x->next; x = x->next)
                        ;
                if (x && x->prop == n->prop) {
                        for (/*nothing*/; x && x->tnext; x = x->tnext)
                                ;
                        x->tnext = n;
                } else {
                        if (x)
                                x->next = n;
                        else {
                                ret = n;
                        }
                }

                /* #of transforms should be updated ? */
        }
    }

        return ret;
}

void
free_proppair(struct prop_pair **pair)
{
        int i;

        for (i = 0; i < MAXPROPPAIRLEN; i++) {
                free_proppair0(pair[i]);
                pair[i] = NULL;
        }
        racoon_free(pair);
}

static void
free_proppair0(struct prop_pair *pair)
{
        struct prop_pair *p, *q, *r, *s;

        p = pair;
        while (p) {
                q = p->next;
                r = p;
                while (r) {
                        s = r->tnext;
                        racoon_free(r);
                        r = s;
                }
                p = q;
        }
}

/*
 * get proposal pairs from SA payload.
 * tiny check for proposal payload.
 */
static struct prop_pair **
get_proppair_and_doi_sit(vchar_t *sa, int mode, uint32_t *doitype,
    uint32_t *sittype)
{
        struct prop_pair **pair = NULL;
        int num_p = 0;                  /* number of proposal for use */
        size_t tlen;
        caddr_t bp;
        int i;
        struct ipsecdoi_sa_b *sab = (struct ipsecdoi_sa_b *)sa->v;

        plog(LLV_DEBUG, LOCATION, NULL, "total SA len=%zu\n", sa->l);
        plogdump(LLV_DEBUG, sa->v, sa->l);

        /* check SA payload size */
        if (sa->l < sizeof(*sab)) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "Invalid SA length = %zu.\n", sa->l);
                goto bad;
        }

        /* check DOI */
        if (check_doi(ntohl(sab->doi)) < 0)
                goto bad;
        if (doitype != NULL)
                *doitype = ntohl(sab->doi);

        /* check SITUATION */
        if (check_situation(ntohl(sab->sit)) < 0)
                goto bad;
        if (sittype != NULL)
                *sittype = ntohl(sab->sit);

        pair = racoon_calloc(1, MAXPROPPAIRLEN * sizeof(*pair));
        if (pair == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get buffer.\n");
                goto bad;
        }

        bp = (caddr_t)(sab + 1);
        tlen = sa->l - sizeof(*sab);

    {
        struct isakmp_pl_p *prop;
        int proplen;
        vchar_t *pbuf = NULL;
        struct isakmp_parse_t *pa;

        pbuf = isakmp_parsewoh(ISAKMP_NPTYPE_P, (struct isakmp_gen *)bp, tlen);
        if (pbuf == NULL)
                goto bad;

        for (pa = (struct isakmp_parse_t *)pbuf->v;
             pa->type != ISAKMP_NPTYPE_NONE;
             pa++) {
                /* check the value of next payload */
                if (pa->type != ISAKMP_NPTYPE_P) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "Invalid payload type=%u\n", pa->type);
                        vfree(pbuf);
                        goto bad;
                }

                prop = (struct isakmp_pl_p *)pa->ptr;
                proplen = pa->len;

                plog(LLV_DEBUG, LOCATION, NULL,
                        "proposal #%u len=%d\n", prop->p_no, proplen);

                if (proplen == 0) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid proposal with length %d\n", proplen);
                        vfree(pbuf);
                        goto bad;
                }

                /* check Protocol ID */
                if (!check_protocol[mode]) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "unsupported mode %d\n", mode);
                        continue;
                }

                if (check_protocol[mode](prop->proto_id) < 0)
                        continue;

                /* check SPI length when IKE. */
                if (check_spi_size(prop->proto_id, prop->spi_size) < 0)
                        continue;

                /* get transform */
                if (get_transform(prop, pair, &num_p) < 0) {
                        vfree(pbuf);
                        goto bad;
                }
        }
        vfree(pbuf);
        pbuf = NULL;
    }

    {
        int notrans, nprop;
        struct prop_pair *p, *q;

        /* check for proposals with no transforms */
        for (i = 0; i < MAXPROPPAIRLEN; i++) {
                if (!pair[i])
                        continue;

                plog(LLV_DEBUG, LOCATION, NULL, "pair %d:\n", i);
                print_proppair(LLV_DEBUG, pair[i]);

                notrans = nprop = 0;
                for (p = pair[i]; p; p = p->next) {
                        if (p->trns == NULL) {
                                notrans++;
                                break;
                        }
                        for (q = p; q; q = q->tnext)
                                nprop++;
                }

#if 0
                /*
                 * XXX at this moment, we cannot accept proposal group
                 * with multiple proposals.  this should be fixed.
                 */
                if (pair[i]->next) {
                        plog(LLV_WARNING, LOCATION, NULL,
                                "proposal #%u ignored "
                                "(multiple proposal not supported)\n",
                                pair[i]->prop->p_no);
                        notrans++;
                }
#endif

                if (notrans) {
                        for (p = pair[i]; p; p = q) {
                                q = p->next;
                                racoon_free(p);
                        }
                        pair[i] = NULL;
                        num_p--;
                } else {
                        plog(LLV_DEBUG, LOCATION, NULL,
                                "proposal #%u: %d transform\n",
                                pair[i]->prop->p_no, nprop);
                }
        }
    }

        /* bark if no proposal is found. */
        if (num_p <= 0) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "no Proposal found.\n");
                goto bad;
        }

        return pair;
bad:
        if (pair != NULL)
                racoon_free(pair);
        return NULL;
}

struct prop_pair **
get_proppair(vchar_t *sa, int mode)
{
        return get_proppair_and_doi_sit(sa, mode, NULL, NULL);
}


/*
 * check transform payload.
 * OUT:
 *      positive: return the pointer to the payload of valid transform.
 *      0       : No valid transform found.
 */
static int
get_transform(struct isakmp_pl_p *prop, struct prop_pair **pair, int *num_p)
{
        int tlen; /* total length of all transform in a proposal */
        caddr_t bp;
        struct isakmp_pl_t *trns;
        int trnslen;
        vchar_t *pbuf = NULL;
        struct isakmp_parse_t *pa;
        struct prop_pair *p = NULL, *q;
        int num_t;

        bp = (caddr_t)prop + sizeof(struct isakmp_pl_p) + prop->spi_size;
        tlen = ntohs(prop->h.len)
                - (sizeof(struct isakmp_pl_p) + prop->spi_size);
        pbuf = isakmp_parsewoh(ISAKMP_NPTYPE_T, (struct isakmp_gen *)bp, tlen);
        if (pbuf == NULL)
                return -1;

        /* check and get transform for use */
        num_t = 0;
        for (pa = (struct isakmp_parse_t *)pbuf->v;
             pa->type != ISAKMP_NPTYPE_NONE;
             pa++) {

                num_t++;

                /* check the value of next payload */
                if (pa->type != ISAKMP_NPTYPE_T) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "Invalid payload type=%u\n", pa->type);
                        break;
                }

                trns = (struct isakmp_pl_t *)pa->ptr;
                trnslen = pa->len;

                plog(LLV_DEBUG, LOCATION, NULL,
                        "transform #%u len=%u\n", trns->t_no, trnslen);

                /* check transform ID */
                if (prop->proto_id >= ARRAYLEN(check_transform)) {
                        plog(LLV_WARNING, LOCATION, NULL,
                                "unsupported proto_id %u\n",
                                prop->proto_id);
                        continue;
                }
                if (prop->proto_id >= ARRAYLEN(check_attributes)) {
                        plog(LLV_WARNING, LOCATION, NULL,
                                "unsupported proto_id %u\n",
                                prop->proto_id);
                        continue;
                }

                if (!check_transform[prop->proto_id]
                 || !check_attributes[prop->proto_id]) {
                        plog(LLV_WARNING, LOCATION, NULL,
                                "unsupported proto_id %u\n",
                                prop->proto_id);
                        continue;
                }
                if (check_transform[prop->proto_id](trns->t_id) < 0)
                        continue;

                /* check data attributes */
                if (check_attributes[prop->proto_id](trns) != 0)
                        continue;

                p = racoon_calloc(1, sizeof(*p));
                if (p == NULL) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "failed to get buffer.\n");
                        vfree(pbuf);
                        return -1;
                }
                p->prop = prop;
                p->trns = trns;

                /* need to preserve the order */
                for (q = pair[prop->p_no]; q && q->next; q = q->next)
                        ;
                if (q && q->prop == p->prop) {
                        for (/*nothing*/; q && q->tnext; q = q->tnext)
                                ;
                        q->tnext = p;
                } else {
                        if (q)
                                q->next = p;
                        else {
                                pair[prop->p_no] = p;
                                (*num_p)++;
                        }
                }
        }

        vfree(pbuf);

        return 0;
}

/*
 * make a new SA payload from prop_pair.
 * NOTE: this function make spi value clear.
 */
vchar_t *
get_sabyproppair(uint32_t doitype, uint32_t sittype, struct prop_pair *pair)
{
        vchar_t *newsa;
        int newtlen;
        uint8_t *np_p = NULL;
        struct prop_pair *p;
        int prophlen, trnslen;
        caddr_t bp;

        newtlen = sizeof(struct ipsecdoi_sa_b);
        for (p = pair; p; p = p->next) {
                newtlen += sizeof(struct isakmp_pl_p);
                newtlen += p->prop->spi_size;
                newtlen += ntohs(p->trns->h.len);
        }

        newsa = vmalloc(newtlen);
        if (newsa == NULL) {
                plog(LLV_ERROR, LOCATION, NULL, "failed to get newsa.\n");
                return NULL;
        }
        bp = newsa->v;

        ((struct isakmp_gen *)bp)->len = htons(newtlen);

        /* update some of values in SA header */
        ((struct ipsecdoi_sa_b *)bp)->doi = htonl(doitype);
        ((struct ipsecdoi_sa_b *)bp)->sit = htonl(sittype);
        bp += sizeof(struct ipsecdoi_sa_b);

        /* create proposal payloads */
        for (p = pair; p; p = p->next) {
                prophlen = sizeof(struct isakmp_pl_p)
                                + p->prop->spi_size;
                trnslen = ntohs(p->trns->h.len);

                if (np_p)
                        *np_p = ISAKMP_NPTYPE_P;

                /* create proposal */

                memcpy(bp, p->prop, prophlen);
                ((struct isakmp_pl_p *)bp)->h.np = ISAKMP_NPTYPE_NONE;
                ((struct isakmp_pl_p *)bp)->h.len = htons(prophlen + trnslen);
                ((struct isakmp_pl_p *)bp)->num_t = 1;
                np_p = &((struct isakmp_pl_p *)bp)->h.np;
                memset(bp + sizeof(struct isakmp_pl_p), 0, p->prop->spi_size);
                bp += prophlen;

                /* create transform */
                memcpy(bp, p->trns, trnslen);
                ((struct isakmp_pl_t *)bp)->h.np = ISAKMP_NPTYPE_NONE;
                ((struct isakmp_pl_t *)bp)->h.len = htons(trnslen);
                bp += trnslen;
        }

        return newsa;
}

/*
 * update responder's spi
 */
int
ipsecdoi_updatespi(struct ph2handle *iph2)
{
        struct prop_pair **pair, *p;
        struct saprop *pp;
        struct saproto *pr;
        int i;
        int error = -1;
        uint8_t *spi;

        pair = get_proppair(iph2->sa_ret, IPSECDOI_TYPE_PH2);
        if (pair == NULL)
                return -1;
        for (i = 0; i < MAXPROPPAIRLEN; i++) {
                if (pair[i])
                        break;
        }
        if (i == MAXPROPPAIRLEN || pair[i]->tnext) {
                /* multiple transform must be filtered by selectph2proposal.*/
                goto end;
        }

        pp = iph2->approval;

        /* create proposal payloads */
        for (p = pair[i]; p; p = p->next) {
                /*
                 * find a proposal/transform with matching proto_id/t_id.
                 * we have analyzed validity already, in cmpsaprop_alloc().
                 */
                for (pr = pp->head; pr; pr = pr->next) {
                        if (p->prop->proto_id == pr->proto_id &&
                            p->trns->t_id == pr->head->trns_id) {
                                break;
                        }
                }
                if (!pr)
                        goto end;

                /*
                 * XXX SPI bits are left-filled, for use with IPComp.
                 * we should be switching to variable-length spi field...
                 */
                spi = (uint8_t *)&pr->spi;
                spi += sizeof(pr->spi);
                spi -= pr->spisize;
                memcpy((caddr_t)p->prop + sizeof(*p->prop), spi, pr->spisize);
        }

        error = 0;
end:
        free_proppair(pair);
        return error;
}

/*
 * make a new SA payload from prop_pair.
 */
vchar_t *
get_sabysaprop(struct saprop *pp0, vchar_t *sa0)
{
        struct prop_pair **pair = NULL;
        vchar_t *newsa = NULL;
        size_t newtlen;
        uint8_t *np_p = NULL;
        struct prop_pair *p = NULL;
        struct saprop *pp;
        struct saproto *pr;
        struct satrns *tr;
        int prophlen, trnslen;
        caddr_t bp;
        int error = -1;

        /* get proposal pair */
        pair = get_proppair(sa0, IPSECDOI_TYPE_PH2);
        if (pair == NULL)
                goto out;

        newtlen = sizeof(struct ipsecdoi_sa_b);
        for (pp = pp0; pp; pp = pp->next) {

                if (pair[pp->prop_no] == NULL)
                        goto out;

                for (pr = pp->head; pr; pr = pr->next) {
                        newtlen += (sizeof(struct isakmp_pl_p)
                                + pr->spisize);

                        for (tr = pr->head; tr; tr = tr->next) {
                                for (p = pair[pp->prop_no]; p; p = p->tnext) {
                                        if (tr->trns_no == p->trns->t_no)
                                                break;
                                }
                                if (p == NULL)
                                        goto out;

                                newtlen += ntohs(p->trns->h.len);
                        }
                }
        }

        newsa = vmalloc(newtlen);
        if (newsa == NULL) {
                plog(LLV_ERROR, LOCATION, NULL, "failed to get newsa.\n");
                goto out;
        }
        bp = newsa->v;

        /* some of values of SA must be updated in the out of this function */
        ((struct isakmp_gen *)bp)->len = htons(newtlen);
        bp += sizeof(struct ipsecdoi_sa_b);

        /* create proposal payloads */
        for (pp = pp0; pp; pp = pp->next) {

                for (pr = pp->head; pr; pr = pr->next) {
                        prophlen = sizeof(struct isakmp_pl_p)
                                        + p->prop->spi_size;

                        for (tr = pr->head; tr; tr = tr->next) {
                                for (p = pair[pp->prop_no]; p; p = p->tnext) {
                                        if (tr->trns_no == p->trns->t_no)
                                                break;
                                }
                                if (p == NULL)
                                        goto out;

                                trnslen = ntohs(p->trns->h.len);

                                if (np_p)
                                        *np_p = ISAKMP_NPTYPE_P;

                                /* create proposal */

                                memcpy(bp, p->prop, prophlen);
                                ((struct isakmp_pl_p *)bp)->h.np = ISAKMP_NPTYPE_NONE;
                                ((struct isakmp_pl_p *)bp)->h.len = htons(prophlen + trnslen);
                                ((struct isakmp_pl_p *)bp)->num_t = 1;
                                np_p = &((struct isakmp_pl_p *)bp)->h.np;
                                bp += prophlen;

                                /* create transform */
                                memcpy(bp, p->trns, trnslen);
                                ((struct isakmp_pl_t *)bp)->h.np = ISAKMP_NPTYPE_NONE;
                                ((struct isakmp_pl_t *)bp)->h.len = htons(trnslen);
                                bp += trnslen;
                        }
                }
        }

        error = 0;
out:
        if (pair != NULL)
                racoon_free(pair);

        if (error != 0) {
                if (newsa != NULL) {
                        vfree(newsa);
                        newsa = NULL;
                }
        }

        return newsa;
}

/*
 * If some error happens then return 0.  Although 0 means that lifetime is zero,
 * such a value should not be accepted.
 * Also 0 of lifebyte should not be included in a packet although 0 means not
 * to care of it.
 */
static uint32_t
ipsecdoi_set_ld(vchar_t *buf)
{
        uint32_t ld;

        if (buf == 0)
                return 0;

        switch (buf->l) {
        case 2:
                ld = ntohs(*(uint16_t *)buf->v);
                break;
        case 4:
                ld = ntohl(*(uint32_t *)buf->v);
                break;
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "length %zu of life duration "
                        "isn't supported.\n", buf->l);
                return 0;
        }

        return ld;
}

/*
 * parse responder-lifetime attributes from payload
 */
int
ipsecdoi_parse_responder_lifetime(struct isakmp_pl_n *notify,
    uint32_t *lifetime_sec, uint32_t *lifetime_kb)
{
        struct isakmp_data *d;
        int flag, type, tlen, ld_type = -1;
        uint16_t lorv;
        uint32_t value;

        tlen = ntohs(notify->h.len) - sizeof(*notify) - notify->spi_size;
        d = (struct isakmp_data *)((char *)(notify + 1) +
                notify->spi_size);

        while (tlen >= sizeof(struct isakmp_data)) {
                type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
                flag = ntohs(d->type) & ISAKMP_GEN_MASK;
                lorv = ntohs(d->lorv);

                plog(LLV_DEBUG, LOCATION, NULL,
                        "type=%s, flag=0x%04x, lorv=%s\n",
                        s_ipsecdoi_attr(type), flag,
                        s_ipsecdoi_attr_v(type, lorv));

                switch (type) {
                case IPSECDOI_ATTR_SA_LD_TYPE:
                        if (! flag) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "must be TV when LD_TYPE.\n");
                                return -1;
                        }
                        ld_type = lorv;
                        break;
                case IPSECDOI_ATTR_SA_LD:
                        if (flag)
                                value = lorv;
                        else if (lorv == 2)
                                value = ntohs(*(uint16_t *)(d + 1));
                        else if (lorv == 4)
                                value = ntohl(*(uint32_t *)(d + 1));
                        else {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "payload length %d for lifetime "
                                        "data length is unsupported.\n", lorv);
                                return -1;
                        }

                        switch (ld_type) {
                        case IPSECDOI_ATTR_SA_LD_TYPE_SEC:
                                if (lifetime_sec != NULL)
                                        *lifetime_sec = value;
                                plog(LLV_INFO, LOCATION, NULL,
                                        "received RESPONDER-LIFETIME: %d "
                                        "seconds\n", value);
                                break;
                        case IPSECDOI_ATTR_SA_LD_TYPE_KB:
                                if (lifetime_kb != NULL)
                                        *lifetime_kb = value;
                                plog(LLV_INFO, LOCATION, NULL,
                                        "received RESPONDER-LIFETIME: %d "
                                        "kbytes\n", value);
                                break;
                        default:
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "lifetime data received without "
                                        "lifetime data type.\n");
                                return -1;
                        }
                        break;
                }

                if (flag) {
                        tlen -= sizeof(*d);
                        d = (struct isakmp_data *)((char *)d
                                + sizeof(*d));
                } else {
                        tlen -= (sizeof(*d) + lorv);
                        d = (struct isakmp_data *)((char *)d
                                + sizeof(*d) + lorv);
                }
        }

        return 0;
}


/*%%%*/
/*
 * check DOI
 */
static int
check_doi(uint32_t doi)
{
        switch (doi) {
        case IPSEC_DOI:
                return 0;
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid value of DOI 0x%08x.\n", doi);
                return -1;
        }
        /* NOT REACHED */
}

/*
 * check situation
 */
static int
check_situation(uint32_t sit)
{
        switch (sit) {
        case IPSECDOI_SIT_IDENTITY_ONLY:
                return 0;

        case IPSECDOI_SIT_SECRECY:
        case IPSECDOI_SIT_INTEGRITY:
                plog(LLV_ERROR, LOCATION, NULL,
                        "situation 0x%08x unsupported yet.\n", sit);
                return -1;

        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid situation 0x%08x.\n", sit);
                return -1;
        }
        /* NOT REACHED */
}

/*
 * check protocol id in main mode
 */
static int
check_prot_main(int proto_id)
{
        switch (proto_id) {
        case IPSECDOI_PROTO_ISAKMP:
                return 0;

        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "Illegal protocol id=%u.\n", proto_id);
                return -1;
        }
        /* NOT REACHED */
}

/*
 * check protocol id in quick mode
 */
static int
check_prot_quick(int proto_id)
{
        switch (proto_id) {
        case IPSECDOI_PROTO_IPSEC_AH:
        case IPSECDOI_PROTO_IPSEC_ESP:
                return 0;

        case IPSECDOI_PROTO_IPCOMP:
                return 0;

        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid protocol id %d.\n", proto_id);
                return -1;
        }
        /* NOT REACHED */
}

static int
check_spi_size(int proto_id, int size)
{
        switch (proto_id) {
        case IPSECDOI_PROTO_ISAKMP:
                if (size != 0) {
                        /* WARNING */
                        plog(LLV_WARNING, LOCATION, NULL,
                                "SPI size isn't zero, but IKE proposal.\n");
                }
                return 0;

        case IPSECDOI_PROTO_IPSEC_AH:
        case IPSECDOI_PROTO_IPSEC_ESP:
                if (size != 4) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid SPI size=%d for IPSEC proposal.\n",
                                size);
                        return -1;
                }
                return 0;

        case IPSECDOI_PROTO_IPCOMP:
                if (size != 2 && size != 4) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid SPI size=%d for IPCOMP proposal.\n",
                                size);
                        return -1;
                }
                return 0;

        default:
                /* ??? */
                return -1;
        }
        /* NOT REACHED */
}

/*
 * check transform ID in ISAKMP.
 */
static int
check_trns_isakmp(int t_id)
{
        switch (t_id) {
        case IPSECDOI_KEY_IKE:
                return 0;
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid transform-id=%u in proto_id=%u.\n",
                        t_id, IPSECDOI_KEY_IKE);
                return -1;
        }
        /* NOT REACHED */
}

/*
 * check transform ID in AH.
 */
static int
check_trns_ah(int t_id)
{
        switch (t_id) {
        case IPSECDOI_AH_MD5:
        case IPSECDOI_AH_SHA:
        case IPSECDOI_AH_SHA256:
        case IPSECDOI_AH_SHA384:
        case IPSECDOI_AH_SHA512:
                return 0;
        case IPSECDOI_AH_DES:
                plog(LLV_ERROR, LOCATION, NULL,
                        "not support transform-id=%u in AH.\n", t_id);
                return -1;
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid transform-id=%u in AH.\n", t_id);
                return -1;
        }
        /* NOT REACHED */
}

/*
 * check transform ID in ESP.
 */
static int
check_trns_esp(int t_id)
{
        switch (t_id) {
        case IPSECDOI_ESP_DES:
        case IPSECDOI_ESP_3DES:
        case IPSECDOI_ESP_NULL:
        case IPSECDOI_ESP_RC5:
        case IPSECDOI_ESP_CAST:
        case IPSECDOI_ESP_BLOWFISH:
        case IPSECDOI_ESP_AES:
        case IPSECDOI_ESP_AESGCM16:
        case IPSECDOI_ESP_TWOFISH:
        case IPSECDOI_ESP_CAMELLIA:
                return 0;
        case IPSECDOI_ESP_DES_IV32:
        case IPSECDOI_ESP_DES_IV64:
        case IPSECDOI_ESP_IDEA:
        case IPSECDOI_ESP_3IDEA:
        case IPSECDOI_ESP_RC4:
                plog(LLV_ERROR, LOCATION, NULL,
                        "not support transform-id=%u in ESP.\n", t_id);
                return -1;
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid transform-id=%u in ESP.\n", t_id);
                return -1;
        }
        /* NOT REACHED */
}

/*
 * check transform ID in IPCOMP.
 */
static int
check_trns_ipcomp(int t_id)
{
        switch (t_id) {
        case IPSECDOI_IPCOMP_OUI:
        case IPSECDOI_IPCOMP_DEFLATE:
        case IPSECDOI_IPCOMP_LZS:
                return 0;
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid transform-id=%u in IPCOMP.\n", t_id);
                return -1;
        }
        /* NOT REACHED */
}

/*
 * check data attributes in IKE.
 */
static int
check_attr_isakmp(struct isakmp_pl_t *trns)
{
        struct isakmp_data *d;
        int tlen;
        int flag, type;
        uint16_t lorv;

        tlen = ntohs(trns->h.len) - sizeof(struct isakmp_pl_t);
        d = (struct isakmp_data *)((caddr_t)trns + sizeof(struct isakmp_pl_t));

        while (tlen > 0) {
                type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
                flag = ntohs(d->type) & ISAKMP_GEN_MASK;
                lorv = ntohs(d->lorv);

                plog(LLV_DEBUG, LOCATION, NULL,
                        "type=%s, flag=0x%04x, lorv=%s\n",
                        s_oakley_attr(type), flag,
                        s_oakley_attr_v(type, lorv));

                /*
                 * some of the attributes must be encoded in TV.
                 * see RFC2409 Appendix A "Attribute Classes".
                 */
                switch (type) {
                case OAKLEY_ATTR_ENC_ALG:
                case OAKLEY_ATTR_HASH_ALG:
                case OAKLEY_ATTR_AUTH_METHOD:
                case OAKLEY_ATTR_GRP_DESC:
                case OAKLEY_ATTR_GRP_TYPE:
                case OAKLEY_ATTR_SA_LD_TYPE:
                case OAKLEY_ATTR_PRF:
                case OAKLEY_ATTR_KEY_LEN:
                case OAKLEY_ATTR_FIELD_SIZE:
                        if (!flag) {    /* TLV*/
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "oakley attribute %d must be TV.\n",
                                        type);
                                return -1;
                        }
                        break;
                }

                /* sanity check for TLV.  length must be specified. */
                if (!flag && lorv == 0) {       /*TLV*/
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid length %d for TLV attribute %d.\n",
                                lorv, type);
                        return -1;
                }

                switch (type) {
                case OAKLEY_ATTR_ENC_ALG:
                        if (!alg_oakley_encdef_ok(lorv)) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "invalied encryption algorithm=%d.\n",
                                        lorv);
                                return -1;
                        }
                        break;

                case OAKLEY_ATTR_HASH_ALG:
                        if (!alg_oakley_hashdef_ok(lorv)) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "invalied hash algorithm=%d.\n",
                                        lorv);
                                return -1;
                        }
                        break;

                case OAKLEY_ATTR_AUTH_METHOD:
                        switch (lorv) {
                        case OAKLEY_ATTR_AUTH_METHOD_PSKEY:
                        case OAKLEY_ATTR_AUTH_METHOD_RSASIG:
#ifdef ENABLE_HYBRID
                        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I:
                        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I:
#endif
#if defined(ENABLE_HYBRID) || defined(HAVE_GSSAPI)
                                /* These two authentication method IDs overlap. */
                        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I:
                        /*case OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB:*/
#endif
                                break;
                        case OAKLEY_ATTR_AUTH_METHOD_DSSSIG:
#ifdef ENABLE_HYBRID
                        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R:
                        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R:
                        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R:
                        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I:
                        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R:
                        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I:
                        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R:
                        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_I:
                        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_R:
                        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_I:
                        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_R:
#endif
                        case OAKLEY_ATTR_AUTH_METHOD_RSAENC:
                        case OAKLEY_ATTR_AUTH_METHOD_RSAREV:
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "auth method %s isn't supported.\n",
                                        s_oakley_attr_method(lorv));
                                return -1;
                        default:
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "invalid auth method %d.\n",
                                        lorv);
                                return -1;
                        }
                        break;

                case OAKLEY_ATTR_GRP_DESC:
                        if (!alg_oakley_dhdef_ok(lorv)) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "invalid DH group %d.\n",
                                        lorv);
                                return -1;
                        }
                        break;

                case OAKLEY_ATTR_GRP_TYPE:
                        switch (lorv) {
                        case OAKLEY_ATTR_GRP_TYPE_MODP:
                                break;
                        default:
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "unsupported DH group type %d.\n",
                                        lorv);
                                return -1;
                        }
                        break;

                case OAKLEY_ATTR_GRP_PI:
                case OAKLEY_ATTR_GRP_GEN_ONE:
                        /* sanity checks? */
                        break;

                case OAKLEY_ATTR_GRP_GEN_TWO:
                case OAKLEY_ATTR_GRP_CURVE_A:
                case OAKLEY_ATTR_GRP_CURVE_B:
                        plog(LLV_ERROR, LOCATION, NULL,
                                "attr type=%u isn't supported.\n", type);
                        return -1;

                case OAKLEY_ATTR_SA_LD_TYPE:
                        switch (lorv) {
                        case OAKLEY_ATTR_SA_LD_TYPE_SEC:
                        case OAKLEY_ATTR_SA_LD_TYPE_KB:
                                break;
                        default:
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "invalid life type: %d.\n", lorv);
                                return -1;
                        }
                        break;

                case OAKLEY_ATTR_SA_LD:
                        /* should check the value */
                        break;

                case OAKLEY_ATTR_PRF:
                case OAKLEY_ATTR_KEY_LEN:
                        break;

                case OAKLEY_ATTR_FIELD_SIZE:
                        plog(LLV_ERROR, LOCATION, NULL,
                                "attr type=%u isn't supported.\n", type);
                        return -1;

                case OAKLEY_ATTR_GRP_ORDER:
                        break;

                case OAKLEY_ATTR_GSS_ID:
                        break;

                default:
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid attribute type %d.\n", type);
                        return -1;
                }

                if (flag) {
                        tlen -= sizeof(*d);
                        d = (struct isakmp_data *)((char *)d
                                + sizeof(*d));
                } else {
                        tlen -= (sizeof(*d) + lorv);
                        d = (struct isakmp_data *)((char *)d
                                + sizeof(*d) + lorv);
                }
        }

        return 0;
}

/*
 * check data attributes in IPSEC AH/ESP.
 */
static int
check_attr_ah(struct isakmp_pl_t *trns)
{
        return check_attr_ipsec(IPSECDOI_PROTO_IPSEC_AH, trns);
}

static int
check_attr_esp(struct isakmp_pl_t *trns)
{
        return check_attr_ipsec(IPSECDOI_PROTO_IPSEC_ESP, trns);
}

static int
check_attr_ipsec(int proto_id, struct isakmp_pl_t *trns)
{
        struct isakmp_data *d;
        int tlen;
        int flag, type = 0;
        uint16_t lorv;
        int attrseen[16];       /* XXX magic number */

        tlen = ntohs(trns->h.len) - sizeof(struct isakmp_pl_t);
        d = (struct isakmp_data *)((caddr_t)trns + sizeof(struct isakmp_pl_t));
        memset(attrseen, 0, sizeof(attrseen));

        while (tlen > 0) {
                type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
                flag = ntohs(d->type) & ISAKMP_GEN_MASK;
                lorv = ntohs(d->lorv);

                plog(LLV_DEBUG, LOCATION, NULL,
                        "type=%s, flag=0x%04x, lorv=%s\n",
                        s_ipsecdoi_attr(type), flag,
                        s_ipsecdoi_attr_v(type, lorv));

                if (type < sizeof(attrseen)/sizeof(attrseen[0]))
                        attrseen[type]++;

                switch (type) {
                case IPSECDOI_ATTR_ENC_MODE:
                        if (! flag) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "must be TV when ENC_MODE.\n");
                                return -1;
                        }

                        switch (lorv) {
                        case IPSECDOI_ATTR_ENC_MODE_TUNNEL:
                        case IPSECDOI_ATTR_ENC_MODE_TRNS:
                                break;
#ifdef ENABLE_NATT
                        case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_RFC:
                        case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC:
                        case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_DRAFT:
                        case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_DRAFT:
                                plog(LLV_DEBUG, LOCATION, NULL,
                                     "UDP encapsulation requested\n");
                                break;
#endif
                        default:
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "invalid encryption mode=%u.\n",
                                        lorv);
                                return -1;
                        }
                        break;

                case IPSECDOI_ATTR_AUTH:
                        if (! flag) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "must be TV when AUTH.\n");
                                return -1;
                        }

                        switch (lorv) {
                        case IPSECDOI_ATTR_AUTH_HMAC_MD5:
                                if (proto_id == IPSECDOI_PROTO_IPSEC_AH &&
                                    trns->t_id != IPSECDOI_AH_MD5) {
ahmismatch:
                                        plog(LLV_ERROR, LOCATION, NULL,
                                                "auth algorithm %u conflicts "
                                                "with transform %u.\n",
                                                lorv, trns->t_id);
                                        return -1;
                                }
                                break;
                        case IPSECDOI_ATTR_AUTH_HMAC_SHA1:
                                if (proto_id == IPSECDOI_PROTO_IPSEC_AH) {
                                        if (trns->t_id != IPSECDOI_AH_SHA)
                                                goto ahmismatch;
                                }
                                break;
                        case IPSECDOI_ATTR_AUTH_HMAC_SHA2_256:
                                if (proto_id == IPSECDOI_PROTO_IPSEC_AH) {
                                        if (trns->t_id != IPSECDOI_AH_SHA256)
                                                goto ahmismatch;
                                }
                                break;
                        case IPSECDOI_ATTR_AUTH_HMAC_SHA2_384:
                                if (proto_id == IPSECDOI_PROTO_IPSEC_AH) {
                                        if (trns->t_id != IPSECDOI_AH_SHA384)
                                                goto ahmismatch;
                                }
                                break;
                        case IPSECDOI_ATTR_AUTH_HMAC_SHA2_512:
                                if (proto_id == IPSECDOI_PROTO_IPSEC_AH) {
                                        if (trns->t_id != IPSECDOI_AH_SHA512)
                                        goto ahmismatch;
                                }
                                break;
                        case IPSECDOI_ATTR_AUTH_DES_MAC:
                        case IPSECDOI_ATTR_AUTH_KPDK:
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "auth algorithm %u isn't supported.\n",
                                        lorv);
                                return -1;
                        default:
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "invalid auth algorithm=%u.\n",
                                        lorv);
                                return -1;
                        }
                        break;

                case IPSECDOI_ATTR_SA_LD_TYPE:
                        if (! flag) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "must be TV when LD_TYPE.\n");
                                return -1;
                        }

                        switch (lorv) {
                        case IPSECDOI_ATTR_SA_LD_TYPE_SEC:
                        case IPSECDOI_ATTR_SA_LD_TYPE_KB:
                                break;
                        default:
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "invalid life type %d.\n", lorv);
                                return -1;
                        }
                        break;

                case IPSECDOI_ATTR_SA_LD:
                        if (flag) {
                                /* i.e. ISAKMP_GEN_TV */
                                plog(LLV_DEBUG, LOCATION, NULL,
                                        "life duration was in TLV.\n");
                        } else {
                                /* i.e. ISAKMP_GEN_TLV */
                                if (lorv == 0) {
                                        plog(LLV_ERROR, LOCATION, NULL,
                                                "invalid length of LD\n");
                                        return -1;
                                }
                        }
                        break;

                case IPSECDOI_ATTR_GRP_DESC:
                        if (! flag) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "must be TV when GRP_DESC.\n");
                                return -1;
                        }

                        if (!alg_oakley_dhdef_ok(lorv)) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "invalid group description=%u.\n",
                                        lorv);
                                return -1;
                        }
                        break;

                case IPSECDOI_ATTR_KEY_LENGTH:
                        if (! flag) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "must be TV when KEY_LENGTH.\n");
                                return -1;
                        }
                        break;

#ifdef HAVE_SECCTX
                case IPSECDOI_ATTR_SECCTX:
                        if (flag) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "SECCTX must be in TLV.\n");
                                return -1;
                        }
                break;
#endif

                case IPSECDOI_ATTR_KEY_ROUNDS:
                case IPSECDOI_ATTR_COMP_DICT_SIZE:
                case IPSECDOI_ATTR_COMP_PRIVALG:
                        plog(LLV_ERROR, LOCATION, NULL,
                                "attr type=%u isn't supported.\n", type);
                        return -1;

                default:
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid attribute type %d.\n", type);
                        return -1;
                }

                if (flag) {
                        tlen -= sizeof(*d);
                        d = (struct isakmp_data *)((char *)d
                                + sizeof(*d));
                } else {
                        tlen -= (sizeof(*d) + lorv);
                        d = (struct isakmp_data *)((caddr_t)d
                                + sizeof(*d) + lorv);
                }
        }

        if (proto_id == IPSECDOI_PROTO_IPSEC_AH &&
            !attrseen[IPSECDOI_ATTR_AUTH]) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "attr AUTH must be present for AH.\n");
                return -1;
        }

        if (proto_id == IPSECDOI_PROTO_IPSEC_ESP &&
            trns->t_id == IPSECDOI_ESP_NULL &&
            !attrseen[IPSECDOI_ATTR_AUTH]) {
                plog(LLV_ERROR, LOCATION, NULL,
                    "attr AUTH must be present for ESP NULL encryption.\n");
                return -1;
        }

        return 0;
}

static int
check_attr_ipcomp(struct isakmp_pl_t *trns)
{
        struct isakmp_data *d;
        int tlen;
        int flag, type = 0;
        uint16_t lorv;
        int attrseen[16];       /* XXX magic number */

        tlen = ntohs(trns->h.len) - sizeof(struct isakmp_pl_t);
        d = (struct isakmp_data *)((caddr_t)trns + sizeof(struct isakmp_pl_t));
        memset(attrseen, 0, sizeof(attrseen));

        while (tlen > 0) {
                type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
                flag = ntohs(d->type) & ISAKMP_GEN_MASK;
                lorv = ntohs(d->lorv);

                plog(LLV_DEBUG, LOCATION, NULL,
                        "type=%d, flag=0x%04x, lorv=0x%04x\n",
                        type, flag, lorv);

                if (type < sizeof(attrseen)/sizeof(attrseen[0]))
                        attrseen[type]++;

                switch (type) {
                case IPSECDOI_ATTR_ENC_MODE:
                        if (! flag) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "must be TV when ENC_MODE.\n");
                                return -1;
                        }

                        switch (lorv) {
                        case IPSECDOI_ATTR_ENC_MODE_TUNNEL:
                        case IPSECDOI_ATTR_ENC_MODE_TRNS:
                                break;
#ifdef ENABLE_NATT
                        case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_RFC:
                        case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC:
                        case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_DRAFT:
                        case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_DRAFT:
                                plog(LLV_DEBUG, LOCATION, NULL,
                                     "UDP encapsulation requested\n");
                                break;
#endif
                        default:
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "invalid encryption mode=%u.\n",
                                        lorv);
                                return -1;
                        }
                        break;

                case IPSECDOI_ATTR_SA_LD_TYPE:
                        if (! flag) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "must be TV when LD_TYPE.\n");
                                return -1;
                        }

                        switch (lorv) {
                        case IPSECDOI_ATTR_SA_LD_TYPE_SEC:
                        case IPSECDOI_ATTR_SA_LD_TYPE_KB:
                                break;
                        default:
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "invalid life type %d.\n", lorv);
                                return -1;
                        }
                        break;

                case IPSECDOI_ATTR_SA_LD:
                        if (flag) {
                                /* i.e. ISAKMP_GEN_TV */
                                plog(LLV_DEBUG, LOCATION, NULL,
                                        "life duration was in TLV.\n");
                        } else {
                                /* i.e. ISAKMP_GEN_TLV */
                                if (lorv == 0) {
                                        plog(LLV_ERROR, LOCATION, NULL,
                                                "invalid length of LD\n");
                                        return -1;
                                }
                        }
                        break;

                case IPSECDOI_ATTR_GRP_DESC:
                        if (! flag) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "must be TV when GRP_DESC.\n");
                                return -1;
                        }

                        if (!alg_oakley_dhdef_ok(lorv)) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "invalid group description=%u.\n",
                                        lorv);
                                return -1;
                        }
                        break;

                case IPSECDOI_ATTR_AUTH:
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid attr type=%u.\n", type);
                        return -1;

                case IPSECDOI_ATTR_KEY_LENGTH:
                case IPSECDOI_ATTR_KEY_ROUNDS:
                case IPSECDOI_ATTR_COMP_DICT_SIZE:
                case IPSECDOI_ATTR_COMP_PRIVALG:
                        plog(LLV_ERROR, LOCATION, NULL,
                                "attr type=%u isn't supported.\n", type);
                        return -1;

                default:
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid attribute type %d.\n", type);
                        return -1;
                }

                if (flag) {
                        tlen -= sizeof(*d);
                        d = (struct isakmp_data *)((char *)d
                                + sizeof(*d));
                } else {
                        tlen -= (sizeof(*d) + lorv);
                        d = (struct isakmp_data *)((caddr_t)d
                                + sizeof(*d) + lorv);
                }
        }

#if 0
        if (proto_id == IPSECDOI_PROTO_IPCOMP &&
            !attrseen[IPSECDOI_ATTR_AUTH]) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "attr AUTH must be present for AH.\n", type);
                return -1;
        }
#endif

        return 0;
}

/* %%% */
/*
 * create phase1 proposal from remote configuration.
 * NOT INCLUDING isakmp general header of SA payload
 */
vchar_t *
ipsecdoi_setph1proposal(struct remoteconf *rmconf, struct isakmpsa *props)
{
        vchar_t *mysa;
        int sablen;

        /* count total size of SA minus isakmp general header */
        /* not including isakmp general header of SA payload */
        sablen = sizeof(struct ipsecdoi_sa_b);
        sablen += setph1prop(props, NULL);

        mysa = vmalloc(sablen);
        if (mysa == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to allocate my sa buffer\n");
                return NULL;
        }

        /* create SA payload */
        /* not including isakmp general header */
        ((struct ipsecdoi_sa_b *)mysa->v)->doi = htonl(rmconf->doitype);
        ((struct ipsecdoi_sa_b *)mysa->v)->sit = htonl(rmconf->sittype);

        (void)setph1prop(props, mysa->v + sizeof(struct ipsecdoi_sa_b));

        return mysa;
}

static int
setph1prop(struct isakmpsa *props, caddr_t buf)
{
        struct isakmp_pl_p *prop = NULL;
        struct isakmpsa *s = NULL;
        int proplen, trnslen;
        uint8_t *np_t; /* pointer next trns type in previous header */
        int trns_num;
        caddr_t p = buf;

        proplen = sizeof(*prop);
        if (buf) {
                /* create proposal */
                prop = (struct isakmp_pl_p *)p;
                prop->h.np = ISAKMP_NPTYPE_NONE;
                prop->p_no = props->prop_no;
                prop->proto_id = IPSECDOI_PROTO_ISAKMP;
                prop->spi_size = 0;
                p += sizeof(*prop);
        }

        np_t = NULL;
        trns_num = 0;

        for (s = props; s != NULL; s = s->next) {
                if (np_t)
                        *np_t = ISAKMP_NPTYPE_T;

                trnslen = setph1trns(s, p);
                proplen += trnslen;
                if (buf) {
                        /* save buffer to pre-next payload */
                        np_t = &((struct isakmp_pl_t *)p)->h.np;
                        p += trnslen;

                        /* count up transform length */
                        trns_num++;
                }
        }

        /* update proposal length */
        if (buf) {
                prop->h.len = htons(proplen);
                prop->num_t = trns_num;
        }

        return proplen;
}

static int
setph1trns(struct isakmpsa *sa, caddr_t buf)
{
        struct isakmp_pl_t *trns = NULL;
        int trnslen, attrlen;
        caddr_t p = buf;

        trnslen = sizeof(*trns);
        if (buf) {
                /* create transform */
                trns = (struct isakmp_pl_t *)p;
                trns->h.np  = ISAKMP_NPTYPE_NONE;
                trns->t_no  = sa->trns_no;
                trns->t_id  = IPSECDOI_KEY_IKE;
                p += sizeof(*trns);
        }

        attrlen = setph1attr(sa, p);
        trnslen += attrlen;
        if (buf)
                p += attrlen;

        if (buf)
                trns->h.len = htons(trnslen);

        return trnslen;
}

static int
setph1attr(struct isakmpsa *sa, caddr_t buf)
{
        caddr_t p = buf;
        int attrlen = 0;

        if (sa->lifetime) {
                uint32_t lifetime = htonl((uint32_t)sa->lifetime);

                attrlen += sizeof(struct isakmp_data)
                        + sizeof(struct isakmp_data);
                if (sa->lifetime > 0xffff)
                        attrlen += sizeof(lifetime);
                if (buf) {
                        p = isakmp_set_attr_l(p, OAKLEY_ATTR_SA_LD_TYPE,
                                                OAKLEY_ATTR_SA_LD_TYPE_SEC);
                        if (sa->lifetime > 0xffff) {
                                p = isakmp_set_attr_v(p, OAKLEY_ATTR_SA_LD,
                                                (caddr_t)&lifetime,
                                                sizeof(lifetime));
                        } else {
                                p = isakmp_set_attr_l(p, OAKLEY_ATTR_SA_LD,
                                                        sa->lifetime);
                        }
                }
        }

        if (sa->lifebyte) {
                uint32_t lifebyte = htonl((uint32_t)sa->lifebyte);

                attrlen += sizeof(struct isakmp_data)
                        + sizeof(struct isakmp_data);
                if (sa->lifebyte > 0xffff)
                        attrlen += sizeof(lifebyte);
                if (buf) {
                        p = isakmp_set_attr_l(p, OAKLEY_ATTR_SA_LD_TYPE,
                                                OAKLEY_ATTR_SA_LD_TYPE_KB);
                        if (sa->lifebyte > 0xffff) {
                                p = isakmp_set_attr_v(p, OAKLEY_ATTR_SA_LD,
                                                        (caddr_t)&lifebyte,
                                                        sizeof(lifebyte));
                        } else {
                                p = isakmp_set_attr_l(p, OAKLEY_ATTR_SA_LD,
                                                        sa->lifebyte);
                        }
                }
        }

        if (sa->enctype) {
                attrlen += sizeof(struct isakmp_data);
                if (buf)
                        p = isakmp_set_attr_l(p, OAKLEY_ATTR_ENC_ALG, sa->enctype);
        }
        if (sa->encklen) {
                attrlen += sizeof(struct isakmp_data);
                if (buf)
                        p = isakmp_set_attr_l(p, OAKLEY_ATTR_KEY_LEN, sa->encklen);
        }
        if (sa->authmethod) {
                int authmethod;

                authmethod = isakmpsa_switch_authmethod(sa->authmethod);
                authmethod &= 0xffff;
                attrlen += sizeof(struct isakmp_data);
                if (buf)
                        p = isakmp_set_attr_l(p, OAKLEY_ATTR_AUTH_METHOD, authmethod);
        }
        if (sa->hashtype) {
                attrlen += sizeof(struct isakmp_data);
                if (buf)
                        p = isakmp_set_attr_l(p, OAKLEY_ATTR_HASH_ALG, sa->hashtype);
        }
        switch (sa->dh_group) {
        case OAKLEY_ATTR_GRP_DESC_MODP768:
        case OAKLEY_ATTR_GRP_DESC_MODP1024:
        case OAKLEY_ATTR_GRP_DESC_MODP1536:
        case OAKLEY_ATTR_GRP_DESC_MODP2048:
        case OAKLEY_ATTR_GRP_DESC_MODP3072:
        case OAKLEY_ATTR_GRP_DESC_MODP4096:
        case OAKLEY_ATTR_GRP_DESC_MODP6144:
        case OAKLEY_ATTR_GRP_DESC_MODP8192:
                /* don't attach group type for known groups */
                attrlen += sizeof(struct isakmp_data);
                if (buf) {
                        p = isakmp_set_attr_l(p, OAKLEY_ATTR_GRP_DESC,
                                sa->dh_group);
                }
                break;
        case OAKLEY_ATTR_GRP_DESC_EC2N155:
        case OAKLEY_ATTR_GRP_DESC_EC2N185:
                /* don't attach group type for known groups */
                attrlen += sizeof(struct isakmp_data);
                if (buf) {
                        p = isakmp_set_attr_l(p, OAKLEY_ATTR_GRP_TYPE,
                                OAKLEY_ATTR_GRP_TYPE_EC2N);
                }
                break;
        case 0:
        default:
                break;
        }

#ifdef HAVE_GSSAPI
        if (sa->authmethod == OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB &&
            sa->gssid != NULL) {
                attrlen += sizeof(struct isakmp_data);
                /*
                 * Older versions of racoon just placed the ISO-Latin-1
                 * string on the wire directly.  Check to see if we are
                 * configured to be compatible with this behavior.  Otherwise,
                 * we encode the GSS ID as UTF-16LE for Windows 2000
                 * compatibility, which requires twice the number of octets.
                 */
                if (lcconf->gss_id_enc == LC_GSSENC_LATIN1)
                        attrlen += sa->gssid->l;
                else
                        attrlen += sa->gssid->l * 2;
                if (buf) {
                        plog(LLV_DEBUG, LOCATION, NULL, "gss id attr: len %zu, "
                            "val '%.*s'\n", sa->gssid->l, (int)sa->gssid->l,
                            sa->gssid->v);
                        if (lcconf->gss_id_enc == LC_GSSENC_LATIN1) {
                                p = isakmp_set_attr_v(p, OAKLEY_ATTR_GSS_ID,
                                        (caddr_t)sa->gssid->v,
                                        sa->gssid->l);
                        } else {
                                size_t dstleft = sa->gssid->l * 2;
                                size_t srcleft = sa->gssid->l;
                                const char *src = (const char *)sa->gssid->v;
                                char *odst, *dst = racoon_malloc(dstleft);
                                iconv_t cd;
                                size_t rv;

                                cd = iconv_open("utf-16le", "latin1");
                                if (cd == (iconv_t) -1) {
                                        plog(LLV_ERROR, LOCATION, NULL,
                                            "unable to initialize "
                                            "latin1 -> utf-16le "
                                            "converstion descriptor: %s\n",
                                            strerror(errno));
                                        attrlen -= sa->gssid->l * 2;
                                        goto gssid_done;
                                }
                                odst = dst;
                                rv = iconv(cd, (__iconv_const char **)(intptr_t)&src,
                                    &srcleft, &dst, &dstleft);
                                if (rv != 0) {
                                        if (rv == (size_t)-1) {
                                                plog(LLV_ERROR, LOCATION, NULL,
                                                    "unable to convert GSS ID "
                                                    "from latin1 -> utf-16le: "
                                                    "%s\n", strerror(errno));
                                        } else {
                                                /* should never happen */
                                                plog(LLV_ERROR, LOCATION, NULL,
                                                    "%zd character%s in GSS ID "
                                                    "cannot be represented "
                                                    "in utf-16le\n",
                                                    rv, rv == 1 ? "" : "s");
                                        }
                                        (void) iconv_close(cd);
                                        attrlen -= sa->gssid->l * 2;
                                        goto gssid_done;
                                }
                                (void) iconv_close(cd);

                                /* XXX Check srcleft and dstleft? */

                                p = isakmp_set_attr_v(p, OAKLEY_ATTR_GSS_ID,
                                        odst, sa->gssid->l * 2);

                                racoon_free(odst);
                        }
                }
        }
 gssid_done:
#endif /* HAVE_GSSAPI */

        return attrlen;
}

static vchar_t *
setph2proposal0(const struct ph2handle *iph2, const struct saprop *pp,
    const struct saproto *pr)
{
        vchar_t *p;
        struct isakmp_pl_p *prop;
        struct isakmp_pl_t *trns;
        struct satrns *tr;
        int attrlen;
        size_t trnsoff;
        caddr_t x;
        uint8_t *np_t; /* pointer next trns type in previous header */
        const uint8_t *spi;
#ifdef HAVE_SECCTX
        int truectxlen = 0;
#endif

        p = vmalloc(sizeof(*prop) + sizeof(pr->spi));
        if (p == NULL)
                return NULL;

        /* create proposal */
        prop = (struct isakmp_pl_p *)p->v;
        prop->h.np = ISAKMP_NPTYPE_NONE;
        prop->p_no = pp->prop_no;
        prop->proto_id = pr->proto_id;
        prop->num_t = 1;

        spi = (const uint8_t *)&pr->spi;
        switch (pr->proto_id) {
        case IPSECDOI_PROTO_IPCOMP:
                /*
                 * draft-shacham-ippcp-rfc2393bis-05.txt:
                 * construct 16bit SPI (CPI).
                 * XXX we may need to provide a configuration option to
                 * generate 32bit SPI.  otherwise we cannot interoeprate
                 * with nodes that uses 32bit SPI, in case we are initiator.
                 */
                prop->spi_size = sizeof(uint16_t);
                spi += sizeof(pr->spi) - sizeof(uint16_t);
                p->l -= sizeof(pr->spi);
                p->l += sizeof(uint16_t);
                break;
        default:
                prop->spi_size = sizeof(pr->spi);
                break;
        }
        memcpy(prop + 1, spi, prop->spi_size);

        /* create transform */
        trnsoff = sizeof(*prop) + prop->spi_size;
        np_t = NULL;

        for (tr = pr->head; tr; tr = tr->next) {

                switch (pr->proto_id) {
                case IPSECDOI_PROTO_IPSEC_ESP:
                        /*
                         * don't build a null encryption
                         * with no authentication transform.
                         */
                        if (tr->trns_id == IPSECDOI_ESP_NULL &&
                            tr->authtype == IPSECDOI_ATTR_AUTH_NONE)
                                continue;
                        break;
                }

                if (np_t) {
                        *np_t = ISAKMP_NPTYPE_T;
                        prop->num_t++;
                }

                /* get attribute length */
                attrlen = 0;
                if (pp->lifetime) {
                        attrlen += sizeof(struct isakmp_data)
                                + sizeof(struct isakmp_data);
                        if (pp->lifetime > 0xffff)
                                attrlen += sizeof(uint32_t);
                }
                if (pp->lifebyte && pp->lifebyte != IPSECDOI_ATTR_SA_LD_KB_MAX) {
                        attrlen += sizeof(struct isakmp_data)
                                + sizeof(struct isakmp_data);
                        if (pp->lifebyte > 0xffff)
                                attrlen += sizeof(uint32_t);
                }
                attrlen += sizeof(struct isakmp_data);  /* enc mode */
                if (tr->encklen)
                        attrlen += sizeof(struct isakmp_data);

                switch (pr->proto_id) {
                case IPSECDOI_PROTO_IPSEC_ESP:
                        /* non authentication mode ? */
                        if (tr->authtype != IPSECDOI_ATTR_AUTH_NONE)
                                attrlen += sizeof(struct isakmp_data);
                        break;
                case IPSECDOI_PROTO_IPSEC_AH:
                        if (tr->authtype == IPSECDOI_ATTR_AUTH_NONE) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "no authentication algorithm found "
                                        "but protocol is AH.\n");
                                vfree(p);
                                return NULL;
                        }
                        attrlen += sizeof(struct isakmp_data);
                        break;
                case IPSECDOI_PROTO_IPCOMP:
                        break;
                default:
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid protocol: %d\n", pr->proto_id);
                        vfree(p);
                        return NULL;
                }

                if (alg_oakley_dhdef_ok(iph2->sainfo->pfs_group))
                        attrlen += sizeof(struct isakmp_data);

#ifdef HAVE_SECCTX
                /* ctx_str is defined as char ctx_str[MAX_CTXSTR_SIZ].
                 * The string may be smaller than MAX_CTXSTR_SIZ.
                 */
                if (*pp->sctx.ctx_str) {
                        truectxlen = sizeof(struct security_ctx) -
                                     (MAX_CTXSTR_SIZE - pp->sctx.ctx_strlen);
                        attrlen += sizeof(struct isakmp_data) + truectxlen;
                }
#endif /* HAVE_SECCTX */

                p = vrealloc(p, p->l + sizeof(*trns) + attrlen);
                if (p == NULL)
                        return NULL;
                prop = (struct isakmp_pl_p *)p->v;

                /* set transform's values */
                trns = (struct isakmp_pl_t *)(p->v + trnsoff);
                trns->h.np  = ISAKMP_NPTYPE_NONE;
                trns->t_no  = tr->trns_no;
                trns->t_id  = tr->trns_id;

                /* set attributes */
                x = p->v + trnsoff + sizeof(*trns);

                if (pp->lifetime) {
                        x = isakmp_set_attr_l(x, IPSECDOI_ATTR_SA_LD_TYPE,
                                                IPSECDOI_ATTR_SA_LD_TYPE_SEC);
                        if (pp->lifetime > 0xffff) {
                                uint32_t v = htonl((uint32_t)pp->lifetime);
                                x = isakmp_set_attr_v(x, IPSECDOI_ATTR_SA_LD,
                                                        (caddr_t)&v, sizeof(v));
                        } else {
                                x = isakmp_set_attr_l(x, IPSECDOI_ATTR_SA_LD,
                                                        pp->lifetime);
                        }
                }

                if (pp->lifebyte && pp->lifebyte != IPSECDOI_ATTR_SA_LD_KB_MAX) {
                        x = isakmp_set_attr_l(x, IPSECDOI_ATTR_SA_LD_TYPE,
                                                IPSECDOI_ATTR_SA_LD_TYPE_KB);
                        if (pp->lifebyte > 0xffff) {
                                uint32_t v = htonl((uint32_t)pp->lifebyte);
                                x = isakmp_set_attr_v(x, IPSECDOI_ATTR_SA_LD,
                                                        (caddr_t)&v, sizeof(v));
                        } else {
                                x = isakmp_set_attr_l(x, IPSECDOI_ATTR_SA_LD,
                                                        pp->lifebyte);
                        }
                }

                x = isakmp_set_attr_l(x, IPSECDOI_ATTR_ENC_MODE, pr->encmode);

                if (tr->encklen)
                        x = isakmp_set_attr_l(x, IPSECDOI_ATTR_KEY_LENGTH, tr->encklen);

                /* mandatory check has done above. */
                if ((pr->proto_id == IPSECDOI_PROTO_IPSEC_ESP && tr->authtype != IPSECDOI_ATTR_AUTH_NONE)
                 || pr->proto_id == IPSECDOI_PROTO_IPSEC_AH)
                        x = isakmp_set_attr_l(x, IPSECDOI_ATTR_AUTH, tr->authtype);

                if (alg_oakley_dhdef_ok(iph2->sainfo->pfs_group))
                        x = isakmp_set_attr_l(x, IPSECDOI_ATTR_GRP_DESC,
                                iph2->sainfo->pfs_group);

#ifdef HAVE_SECCTX
                if (*pp->sctx.ctx_str) {
                        struct security_ctx secctx;
                        secctx = pp->sctx;
                        secctx.ctx_strlen = htons(pp->sctx.ctx_strlen);
                        x = isakmp_set_attr_v(x, IPSECDOI_ATTR_SECCTX,
                                             (caddr_t)&secctx, truectxlen);
                }
#endif
                /* update length of this transform. */
                trns = (struct isakmp_pl_t *)(p->v + trnsoff);
                trns->h.len = htons(sizeof(*trns) + attrlen);

                /* save buffer to pre-next payload */
                np_t = &trns->h.np;

                trnsoff += (sizeof(*trns) + attrlen);
        }

        if (np_t == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "no suitable proposal was created.\n");
                return NULL;
        }

        /* update length of this protocol. */
        prop->h.len = htons(p->l);

        return p;
}

/*
 * create phase2 proposal from policy configuration.
 * NOT INCLUDING isakmp general header of SA payload.
 * This function is called by initiator only.
 */
int
ipsecdoi_setph2proposal(struct ph2handle *iph2)
{
        struct saprop *proposal, *a;
        struct saproto *b = NULL;
        vchar_t *q;
        struct ipsecdoi_sa_b *sab;
        struct isakmp_pl_p *prop;
        size_t propoff; /* for previous field of type of next payload. */

        proposal = iph2->proposal;

        iph2->sa = vmalloc(sizeof(*sab));
        if (iph2->sa == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to allocate my sa buffer\n");
                return -1;
        }

        /* create SA payload */
        sab = (struct ipsecdoi_sa_b *)iph2->sa->v;
        sab->doi = htonl(IPSEC_DOI);
        sab->sit = htonl(IPSECDOI_SIT_IDENTITY_ONLY);   /* XXX configurable ? */

        prop = NULL;
        propoff = 0;
        for (a = proposal; a; a = a->next) {
                for (b = a->head; b; b = b->next) {
#ifdef ENABLE_NATT
                        if (iph2->ph1->natt_flags & NAT_DETECTED) {
                          int udp_diff = iph2->ph1->natt_options->mode_udp_diff;
                          plog (LLV_INFO, LOCATION, NULL,
                                "NAT detected -> UDP encapsulation "
                                "(ENC_MODE %d->%d).\n",
                                b->encmode,
                                b->encmode+udp_diff);
                          /* Tunnel -> UDP-Tunnel, Transport -> UDP_Transport */
                          b->encmode += udp_diff;
                          b->udp_encap = 1;
                        }
#endif

                        q = setph2proposal0(iph2, a, b);
                        if (q == NULL) {
                                VPTRINIT(iph2->sa);
                                return -1;
                        }

                        iph2->sa = vrealloc(iph2->sa, iph2->sa->l + q->l);
                        if (iph2->sa == NULL) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "failed to allocate my sa buffer\n");
                                if (q)
                                        vfree(q);
                                return -1;
                        }
                        memcpy(iph2->sa->v + iph2->sa->l - q->l, q->v, q->l);
                        if (propoff != 0) {
                                prop = (struct isakmp_pl_p *)(iph2->sa->v +
                                        propoff);
                                prop->h.np = ISAKMP_NPTYPE_P;
                        }
                        propoff = iph2->sa->l - q->l;

                        vfree(q);
                }
        }

        return 0;
}

/*
 * return 1 if all of the given protocols are transport mode.
 */
int
ipsecdoi_transportmode(struct saprop *pp)
{
        struct saproto *pr = NULL;

        for (; pp; pp = pp->next) {
                for (pr = pp->head; pr; pr = pr->next) {
                        if (pr->encmode != IPSECDOI_ATTR_ENC_MODE_TRNS &&
                            pr->encmode != IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC &&
                            pr->encmode != IPSECDOI_ATTR_ENC_MODE_UDPTRNS_DRAFT)
                                return 0;
                }
        }

        return 1;
}

int
ipsecdoi_get_defaultlifetime(void)
{
        return IPSECDOI_ATTR_SA_LD_SEC_DEFAULT;
}

int
ipsecdoi_checkalgtypes(int proto_id, int enc, int auth, int comp)
{
#define TMPALGTYPE2STR(n) s_algtype(algclass_ipsec_##n, n)
        switch (proto_id) {
        case IPSECDOI_PROTO_IPSEC_ESP:
                if (enc == 0 || comp != 0) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "illegal algorithm defined "
                                "ESP enc=%s auth=%s comp=%s.\n",
                                TMPALGTYPE2STR(enc),
                                TMPALGTYPE2STR(auth),
                                TMPALGTYPE2STR(comp));
                        return -1;
                }
                break;
        case IPSECDOI_PROTO_IPSEC_AH:
                if (enc != 0 || auth == 0 || comp != 0) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "illegal algorithm defined "
                                "AH enc=%s auth=%s comp=%s.\n",
                                TMPALGTYPE2STR(enc),
                                TMPALGTYPE2STR(auth),
                                TMPALGTYPE2STR(comp));
                        return -1;
                }
                break;
        case IPSECDOI_PROTO_IPCOMP:
                if (enc != 0 || auth != 0 || comp == 0) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "illegal algorithm defined "
                                "IPcomp enc=%s auth=%s comp=%s.\n",
                                TMPALGTYPE2STR(enc),
                                TMPALGTYPE2STR(auth),
                                TMPALGTYPE2STR(comp));
                        return -1;
                }
                break;
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid ipsec protocol %d\n", proto_id);
                return -1;
        }
#undef TMPALGTYPE2STR
        return 0;
}

int
ipproto2doi(int proto)
{
        switch (proto) {
        case IPPROTO_AH:
                return IPSECDOI_PROTO_IPSEC_AH;
        case IPPROTO_ESP:
                return IPSECDOI_PROTO_IPSEC_ESP;
        case IPPROTO_IPCOMP:
                return IPSECDOI_PROTO_IPCOMP;
        }
        return -1;      /* XXX */
}

int
doi2ipproto(int proto)
{
        switch (proto) {
        case IPSECDOI_PROTO_IPSEC_AH:
                return IPPROTO_AH;
        case IPSECDOI_PROTO_IPSEC_ESP:
                return IPPROTO_ESP;
        case IPSECDOI_PROTO_IPCOMP:
                return IPPROTO_IPCOMP;
        }
        return -1;      /* XXX */
}

/*
 * Check if a subnet id is valid for comparison
 * with an address id ( address length mask )
 * and compare them
 * Return value
 * =  0 for match
 * =  1 for mismatch
 */

static int
ipsecdoi_subnetisaddr_v4(const vchar_t *subnet, const vchar_t *address)
{
        struct in_addr *mask;

        if (address->l != sizeof(struct in_addr))
                return 1;

        if (subnet->l != (sizeof(struct in_addr)*2))
                return 1;

        mask = (struct in_addr*)(subnet->v + sizeof(struct in_addr));

        if (mask->s_addr!=0xffffffff)
                return 1;

        return memcmp(subnet->v,address->v,address->l);
}

#ifdef INET6

static int
ipsecdoi_subnetisaddr_v6(const vchar_t *subnet, const vchar_t *address)
{
        struct in6_addr *mask;
        int i;

        if (address->l != sizeof(struct in6_addr))
                return 1;

        if (subnet->l != (sizeof(struct in6_addr)*2))
                return 1;

        mask = (struct in6_addr*)(subnet->v + sizeof(struct in6_addr));

        for (i=0; i<16; i++)
                if(mask->s6_addr[i]!=0xff)
                        return 1;

        return memcmp(subnet->v,address->v,address->l);
}

#endif

/*
 * Check and Compare two IDs
 * - specify 0 for exact if wildcards are allowed
 * Return value
 * =  0 for match
 * =  1 for misatch
 * = -1 for integrity error
 */

int
ipsecdoi_chkcmpids(const vchar_t *idt, const vchar_t *ids, int exact)
{
        struct ipsecdoi_id_b *id_bt;
        struct ipsecdoi_id_b *id_bs;
        vchar_t ident_t;
        vchar_t ident_s;
        int result;

        /* handle wildcard IDs */

        if (idt == NULL || ids == NULL)
        {
                if( !exact )
                {
                        plog(LLV_DEBUG, LOCATION, NULL,
                                "check and compare ids : values matched (ANONYMOUS)\n" );
                        return 0;
                }
                else
                {
                        plog(LLV_DEBUG, LOCATION, NULL,
                                "check and compare ids : value mismatch (ANONYMOUS)\n" );
                        return -1;
                }
        }

        /* make sure the ids are of the same type */

        id_bt = (struct ipsecdoi_id_b *) idt->v;
        id_bs = (struct ipsecdoi_id_b *) ids->v;

        ident_t.v = idt->v + sizeof(*id_bt);
        ident_t.l = idt->l - sizeof(*id_bt);
        ident_s.v = ids->v + sizeof(*id_bs);
        ident_s.l = ids->l - sizeof(*id_bs);

        if (id_bs->type != id_bt->type)
        {
                /*
                 * special exception for comparing
                 * address to subnet id types when
                 * the netmask is address length
                 */

                if ((id_bs->type == IPSECDOI_ID_IPV4_ADDR)&&
                    (id_bt->type == IPSECDOI_ID_IPV4_ADDR_SUBNET)) {
                        result = ipsecdoi_subnetisaddr_v4(&ident_t,&ident_s);
                        goto cmpid_result;
                }

                if ((id_bs->type == IPSECDOI_ID_IPV4_ADDR_SUBNET)&&
                    (id_bt->type == IPSECDOI_ID_IPV4_ADDR)) {
                        result = ipsecdoi_subnetisaddr_v4(&ident_s,&ident_t);
                        goto cmpid_result;
                }

#ifdef INET6
                if ((id_bs->type == IPSECDOI_ID_IPV6_ADDR)&&
                    (id_bt->type == IPSECDOI_ID_IPV6_ADDR_SUBNET)) {
                        result = ipsecdoi_subnetisaddr_v6(&ident_t,&ident_s);
                        goto cmpid_result;
                }

                if ((id_bs->type == IPSECDOI_ID_IPV6_ADDR_SUBNET)&&
                    (id_bt->type == IPSECDOI_ID_IPV6_ADDR)) {
                        result = ipsecdoi_subnetisaddr_v6(&ident_s,&ident_t);
                        goto cmpid_result;
                }
#endif
                plog(LLV_DEBUG, LOCATION, NULL,
                        "check and compare ids : id type mismatch %s != %s\n",
                        s_ipsecdoi_ident(id_bs->type),
                        s_ipsecdoi_ident(id_bt->type));

                return 1;
        }

        if(id_bs->proto_id != id_bt->proto_id){
                plog(LLV_DEBUG, LOCATION, NULL,
                        "check and compare ids : proto_id mismatch %d != %d\n",
                        id_bs->proto_id, id_bt->proto_id);

                return 1;
        }

        /* compare the ID data. */

        switch (id_bt->type) {
                case IPSECDOI_ID_DER_ASN1_DN:
                case IPSECDOI_ID_DER_ASN1_GN:
                        /* compare asn1 ids */
                        result = eay_cmp_asn1dn(&ident_t, &ident_s);
                        goto cmpid_result;

                case IPSECDOI_ID_IPV4_ADDR:
                        /* validate lengths */
                        if ((ident_t.l != sizeof(struct in_addr))||
                            (ident_s.l != sizeof(struct in_addr)))
                                goto cmpid_invalid;
                        break;

                case IPSECDOI_ID_IPV4_ADDR_SUBNET:
                case IPSECDOI_ID_IPV4_ADDR_RANGE:
                        /* validate lengths */
                        if ((ident_t.l != (sizeof(struct in_addr)*2))||
                            (ident_s.l != (sizeof(struct in_addr)*2)))
                                goto cmpid_invalid;
                        break;

#ifdef INET6
                case IPSECDOI_ID_IPV6_ADDR:
                        /* validate lengths */
                        if ((ident_t.l != sizeof(struct in6_addr))||
                            (ident_s.l != sizeof(struct in6_addr)))
                                goto cmpid_invalid;
                        break;

                case IPSECDOI_ID_IPV6_ADDR_SUBNET:
                case IPSECDOI_ID_IPV6_ADDR_RANGE:
                        /* validate lengths */
                        if ((ident_t.l != (sizeof(struct in6_addr)*2))||
                            (ident_s.l != (sizeof(struct in6_addr)*2)))
                                goto cmpid_invalid;
                        break;
#endif
                case IPSECDOI_ID_FQDN:
                case IPSECDOI_ID_USER_FQDN:
                case IPSECDOI_ID_KEY_ID:
                        break;

                default:
                        plog(LLV_ERROR, LOCATION, NULL,
                                "Unhandled id type %i specified for comparison\n",
                                id_bt->type);
                        return -1;
        }

        /* validate matching data and length */
        if (ident_t.l == ident_s.l)
                result = memcmp(ident_t.v,ident_s.v,ident_t.l);
        else
                result = 1;

cmpid_result:

        /* debug level output */
        if(loglevel >= LLV_DEBUG) {
                char *idstrt = ipsecdoi_id2str(idt);
                char *idstrs = ipsecdoi_id2str(ids);

                if (!result)
                        plog(LLV_DEBUG, LOCATION, NULL,
                                "check and compare ids : values matched (%s)\n",
                                 s_ipsecdoi_ident(id_bs->type) );
                else
                        plog(LLV_DEBUG, LOCATION, NULL,
                                "check and compare ids : value mismatch (%s)\n",
                                 s_ipsecdoi_ident(id_bs->type));

                plog(LLV_DEBUG, LOCATION, NULL, "cmpid target: \'%s\'\n", idstrt );
                plog(LLV_DEBUG, LOCATION, NULL, "cmpid source: \'%s\'\n", idstrs );

                racoon_free(idstrs);
                racoon_free(idstrt);
        }

        /* return result */
        if( !result )
                return 0;
        else
                return 1;

cmpid_invalid:

        /* id integrity error */
        plog(LLV_DEBUG, LOCATION, NULL, "check and compare ids : %s integrity error\n",
                s_ipsecdoi_ident(id_bs->type));
        plog(LLV_DEBUG, LOCATION, NULL, "cmpid target: length = \'%zu\'\n", ident_t.l );
        plog(LLV_DEBUG, LOCATION, NULL, "cmpid source: length = \'%zu\'\n", ident_s.l );

        return -1;
}

/*
 * check the following:
 * - In main mode with pre-shared key, only address type can be used.
 * - if proper type for phase 1 ?
 * - if phase 1 ID payload conformed RFC2407 4.6.2.
 *   (proto, port) must be (0, 0), (udp, 500) or (udp, [specified]).
 * - if ID payload sent from peer is equal to the ID expected by me.
 *
 * both of "id" and "id_p" should be ID payload without general header,
 */
int
ipsecdoi_checkid1(struct ph1handle *iph1)
{
        struct ipsecdoi_id_b *id_b;

        if (iph1->id_p == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid iph1 passed id_p == NULL\n");
                return ISAKMP_INTERNAL_ERROR;
        }
        if (iph1->id_p->l < sizeof(*id_b)) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid value passed as \"ident\" (len=%lu)\n",
                        (u_long)iph1->id_p->l);
                return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
        }

        id_b = (struct ipsecdoi_id_b *)iph1->id_p->v;

        /* In main mode with pre-shared key, only address type can be used. */
        if (iph1->etype == ISAKMP_ETYPE_IDENT &&
            iph1->approval->authmethod == OAKLEY_ATTR_AUTH_METHOD_PSKEY) {
                 if (id_b->type != IPSECDOI_ID_IPV4_ADDR
                  && id_b->type != IPSECDOI_ID_IPV6_ADDR) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "Expecting IP address type in main mode, "
                                "but %s.\n", s_ipsecdoi_ident(id_b->type));
                        return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
                }
        }

        /* if proper type for phase 1 ? */
        switch (id_b->type) {
        case IPSECDOI_ID_IPV4_ADDR_SUBNET:
        case IPSECDOI_ID_IPV6_ADDR_SUBNET:
        case IPSECDOI_ID_IPV4_ADDR_RANGE:
        case IPSECDOI_ID_IPV6_ADDR_RANGE:
                plog(LLV_WARNING, LOCATION, NULL,
                        "such ID type %s is not proper.\n",
                        s_ipsecdoi_ident(id_b->type));
                /*FALLTHROUGH*/
        }

        /* if phase 1 ID payload conformed RFC2407 4.6.2. */
        if (id_b->type == IPSECDOI_ID_IPV4_ADDR ||
            id_b->type == IPSECDOI_ID_IPV6_ADDR) {

                if (id_b->proto_id == 0 && ntohs(id_b->port) != 0) {
                        plog(LLV_WARNING, LOCATION, NULL,
                                "protocol ID and Port mismatched. "
                                "proto_id:%d port:%d\n",
                                id_b->proto_id, ntohs(id_b->port));
                        /*FALLTHROUGH*/

                } else if (id_b->proto_id == IPPROTO_UDP) {
                        /*
                         * copmaring with expecting port.
                         * always permit if port is equal to PORT_ISAKMP
                         */
                        if (ntohs(id_b->port) != PORT_ISAKMP) {
                                uint16_t port;

                                port = extract_port(iph1->remote);
                                if (ntohs(id_b->port) != port) {
                                        plog(LLV_WARNING, LOCATION, NULL,
                                                "port %d expected, but %d\n",
                                                port, ntohs(id_b->port));
                                        /*FALLTHROUGH*/
                                }
                        }
                }
        }

        /* resolve remote configuration if not done yet */
        if (resolveph1rmconf(iph1) < 0)
                return ISAKMP_NTYPE_INVALID_ID_INFORMATION;

        if (iph1->rmconf == NULL)
                return ISAKMP_NTYPE_INVALID_ID_INFORMATION;

        return 0;
}

/*
 * create ID payload for phase 1 and set into iph1->id.
 * NOT INCLUDING isakmp general header.
 * see, RFC2407 4.6.2.1
 */
int
ipsecdoi_setid1(struct ph1handle *iph1)
{
        vchar_t *ret = NULL;
        struct ipsecdoi_id_b id_b;
        vchar_t *ident = NULL;
        struct sockaddr *ipid = NULL;

        /* init */
        id_b.proto_id = 0;
        id_b.port = 0;
        ident = NULL;

        switch (iph1->rmconf->idvtype) {
        case IDTYPE_FQDN:
                id_b.type = IPSECDOI_ID_FQDN;
                ident = vdup(iph1->rmconf->idv);
                break;
        case IDTYPE_USERFQDN:
                id_b.type = IPSECDOI_ID_USER_FQDN;
                ident = vdup(iph1->rmconf->idv);
                break;
        case IDTYPE_KEYID:
                id_b.type = IPSECDOI_ID_KEY_ID;
                ident = vdup(iph1->rmconf->idv);
                break;
        case IDTYPE_ASN1DN:
                id_b.type = IPSECDOI_ID_DER_ASN1_DN;
                if (iph1->rmconf->idv) {
                        /* XXX it must be encoded to asn1dn. */
                        ident = vdup(iph1->rmconf->idv);
                } else {
                        if (oakley_getmycert(iph1) < 0) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "failed to get own CERT.\n");
                                goto err;
                        }
                        ident = eay_get_x509asn1subjectname(iph1->cert);
                }
                break;
        case IDTYPE_ADDRESS:
                /*
                 * if the value of the id type was set by the configuration
                 * file, then use it.  otherwise the value is get from local
                 * ip address by using ike negotiation.
                 */
                if (iph1->rmconf->idv)
                        ipid = (struct sockaddr *)iph1->rmconf->idv->v;
                /*FALLTHROUGH*/
        default:
            {
                int l;
                caddr_t p;

                if (ipid == NULL)
                        ipid = iph1->local;

                /* use IP address */
                switch (ipid->sa_family) {
                case AF_INET:
                        id_b.type = IPSECDOI_ID_IPV4_ADDR;
                        l = sizeof(struct in_addr);
                        p = (caddr_t)&((struct sockaddr_in *)ipid)->sin_addr;
                        break;
#ifdef INET6
                case AF_INET6:
                        id_b.type = IPSECDOI_ID_IPV6_ADDR;
                        l = sizeof(struct in6_addr);
                        p = (caddr_t)&((struct sockaddr_in6 *)ipid)->sin6_addr;
                        break;
#endif
                default:
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid address family.\n");
                        goto err;
                }
                id_b.proto_id = IPPROTO_UDP;
                id_b.port = htons(PORT_ISAKMP);
                ident = vmalloc(l);
                if (!ident) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "failed to get ID buffer.\n");
                        return -1;
                }
                memcpy(ident->v, p, ident->l);
            }
        }
        if (!ident) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get ID buffer.\n");
                return -1;
        }

        ret = vmalloc(sizeof(id_b) + ident->l);
        if (ret == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get ID buffer.\n");
                goto err;
        }

        memcpy(ret->v, &id_b, sizeof(id_b));
        memcpy(ret->v + sizeof(id_b), ident->v, ident->l);

        iph1->id = ret;

        plog(LLV_DEBUG, LOCATION, NULL,
                "use ID type of %s\n", s_ipsecdoi_ident(id_b.type));
        if (ident)
                vfree(ident);
        return 0;

err:
        if (ident)
                vfree(ident);
        plog(LLV_ERROR, LOCATION, NULL, "failed get my ID\n");
        return -1;
}

/* it's only called by cfparse.y. */
int
set_identifier(vchar_t **vpp, int type, const vchar_t * const value)
{
        return set_identifier_qual(vpp, type, value, IDQUAL_UNSPEC);
}

int
set_identifier_qual(vchar_t **vpp, int type, const vchar_t * const value,
    int qual)
{
        vchar_t *new = NULL;

        /* simply return if value is null. */
        if (!value){
                if( type == IDTYPE_FQDN || type == IDTYPE_USERFQDN){
                        plog(LLV_ERROR, LOCATION, NULL,
                                 "No %s\n", type == IDTYPE_FQDN ? "fqdn":"user fqdn");
                        return -1;
                }
                return 0;
        }

        switch (type) {
        case IDTYPE_FQDN:
        case IDTYPE_USERFQDN:
                if(value->l <= 1){
                        plog(LLV_ERROR, LOCATION, NULL,
                                 "Empty %s\n", type == IDTYPE_FQDN ? "fqdn":"user fqdn");
                        return -1;
                }
                /* length is adjusted since QUOTEDSTRING teminates NULL. */
                new = vmalloc(value->l - 1);
                if (new == NULL)
                        return -1;
                memcpy(new->v, value->v, new->l);
                break;
        case IDTYPE_KEYID:
                /*
                 * If no qualifier is specified: IDQUAL_UNSPEC. It means
                 * to use a file for backward compatibility sake.
                 */
                switch(qual) {
                case IDQUAL_FILE:
                case IDQUAL_UNSPEC: {
                        FILE *fp;
                        char b[512];
                        int tlen, len;

                        fp = fopen(value->v, "r");
                        if (fp == NULL) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "can not open %s\n", value->v);
                                return -1;
                        }
                        tlen = 0;
                        while ((len = fread(b, 1, sizeof(b), fp)) != 0) {
                                new = vrealloc(new, tlen + len);
                                if (!new) {
                                        fclose(fp);
                                        return -1;
                                }
                                memcpy(new->v + tlen, b, len);
                                tlen += len;
                        }
                        fclose(fp);
                        break;
                }

                case IDQUAL_TAG:
                        new = vmalloc(value->l - 1);
                        if (new == NULL) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "can not allocate memory");
                                return -1;
                        }
                        memcpy(new->v, value->v, new->l);
                        break;

                default:
                        plog(LLV_ERROR, LOCATION, NULL,
                                "unknown qualifier");
                        return -1;
                }
                break;

        case IDTYPE_ADDRESS: {
                struct sockaddr *sa;

                /* length is adjusted since QUOTEDSTRING teminates NULL. */
                if (value->l == 0)
                        break;

                sa = str2saddr(value->v, NULL);
                if (sa == NULL) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid ip address %s\n", value->v);
                        return -1;
                }

                new = vmalloc(sysdep_sa_len(sa));
                if (new == NULL) {
                        racoon_free(sa);
                        return -1;
                }
                memcpy(new->v, sa, new->l);
                racoon_free(sa);
                break;
        }
        case IDTYPE_ASN1DN:
                if (value->v[0] == '~')
                        /* Hex-encoded ASN1 strings */
                        new = eay_hex2asn1dn(value->v + 1, - 1);
                else
                        /* DN encoded strings */
                        new = eay_str2asn1dn(value->v, value->l - 1);

                if (new == NULL)
                        return -1;

                if (loglevel >= LLV_DEBUG) {
                        X509_NAME *xn;
                        BIO *bio;
                        unsigned char *ptr = (unsigned char *) new->v;
                        size_t len;
                        char save;

                        xn = d2i_X509_NAME(NULL, (void *)&ptr, new->l);
                        bio = BIO_new(BIO_s_mem());

                        X509_NAME_print_ex(bio, xn, 0, 0);
                        len = BIO_get_mem_data(bio, &ptr);
                        save = ptr[len];
                        ptr[len] = 0;
                        plog(LLV_DEBUG, LOCATION, NULL, "Parsed DN: %s\n", ptr);
                        ptr[len] = save;
                        X509_NAME_free(xn);
                        BIO_free(bio);
                }

                break;
        }

        *vpp = new;

        return 0;
}

/*
 * create ID payload for phase 2, and set into iph2->id and id_p.  There are
 * NOT INCLUDING isakmp general header.
 * this function is for initiator.  responder will get to copy from payload.
 * responder ID type is always address type.
 * see, RFC2407 4.6.2.1
 */
int
ipsecdoi_setid2(struct ph2handle *iph2)
{
        struct secpolicy *sp;

        /* check there is phase 2 handler ? */
        sp = getspbyspid(iph2->spid);
        if (sp == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "no policy found for spid:%u.\n", iph2->spid);
                return -1;
        }

        if (!ipsecdoi_transportmode(iph2->proposal))
                iph2->id = ipsecdoi_sockaddr2id((struct sockaddr *)&sp->spidx.src,
                                sp->spidx.prefs, sp->spidx.ul_proto);
        else if (iph2->sa_src != NULL) {
                /* He have a specific hint indicating that the transport
                 * mode SA will be negotiated using addresses that differ
                 * with the one from the SA. We need to indicate that to
                 * our peer by setting the SA address as ID.
                 * This is typically the case for the bootstrapping of the
                 * transport mode SA protecting BU/BA for MIPv6 traffic
                 *
                 * --arno*/
                iph2->id = ipsecdoi_sockaddr2id(iph2->sa_src,
                                                IPSECDOI_PREFIX_HOST,
                                                sp->spidx.ul_proto);
        } else
                iph2->id = ipsecdoi_sockaddr2id(iph2->src, IPSECDOI_PREFIX_HOST,
                                                sp->spidx.ul_proto);

        if (iph2->id == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get ID for %s\n",
                        spidx2str(&sp->spidx));
                return -1;
        }
        plog(LLV_DEBUG, LOCATION, NULL, "use local ID type %s\n",
                s_ipsecdoi_ident(((struct ipsecdoi_id_b *)iph2->id->v)->type));

        /* remote side */
        if (!ipsecdoi_transportmode(iph2->proposal))
                iph2->id_p = ipsecdoi_sockaddr2id((struct sockaddr *)&sp->spidx.dst,
                                sp->spidx.prefd, sp->spidx.ul_proto);
        else if (iph2->sa_dst != NULL) {
                /* See comment above for local side. */
                iph2->id_p = ipsecdoi_sockaddr2id(iph2->sa_dst,
                                                  IPSECDOI_PREFIX_HOST,
                                                  sp->spidx.ul_proto);
        } else
                iph2->id_p = ipsecdoi_sockaddr2id(iph2->dst, IPSECDOI_PREFIX_HOST,
                        sp->spidx.ul_proto);

        if (iph2->id_p == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get ID for %s\n",
                        spidx2str(&sp->spidx));
                VPTRINIT(iph2->id);
                return -1;
        }
        plog(LLV_DEBUG, LOCATION, NULL,
                "use remote ID type %s\n",
                s_ipsecdoi_ident(((struct ipsecdoi_id_b *)iph2->id_p->v)->type));

        return 0;
}

/*
 * set address type of ID.
 * NOT INCLUDING general header.
 */
vchar_t *
ipsecdoi_sockaddr2id(struct sockaddr *saddr, u_int prefixlen, u_int ul_proto)
{
        vchar_t *new;
        int type, len1, len2;
        caddr_t sa;
        u_short port;

        /*
         * Q. When type is SUBNET, is it allowed to be ::1/128.
         * A. Yes. (consensus at bake-off)
         */
        switch (saddr->sa_family) {
        case AF_INET:
                len1 = sizeof(struct in_addr);
                if (prefixlen >= (sizeof(struct in_addr) << 3)) {
                        type = IPSECDOI_ID_IPV4_ADDR;
                        len2 = 0;
                } else {
                        type = IPSECDOI_ID_IPV4_ADDR_SUBNET;
                        len2 = sizeof(struct in_addr);
                }
                sa = (caddr_t)&((struct sockaddr_in *)(saddr))->sin_addr;
                port = ((struct sockaddr_in *)(saddr))->sin_port;
                break;
#ifdef INET6
        case AF_INET6:
                len1 = sizeof(struct in6_addr);
                if (prefixlen >= (sizeof(struct in6_addr) << 3)) {
                        type = IPSECDOI_ID_IPV6_ADDR;
                        len2 = 0;
                } else {
                        type = IPSECDOI_ID_IPV6_ADDR_SUBNET;
                        len2 = sizeof(struct in6_addr);
                }
                sa = (caddr_t)&((struct sockaddr_in6 *)(saddr))->sin6_addr;
                port = ((struct sockaddr_in6 *)(saddr))->sin6_port;
                break;
#endif
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid family: %d.\n", saddr->sa_family);
                return NULL;
        }

        /* get ID buffer */
        new = vmalloc(sizeof(struct ipsecdoi_id_b) + len1 + len2);
        if (new == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get ID buffer.\n");
                return NULL;
        }

        memset(new->v, 0, new->l);

        /* set the part of header. */
        ((struct ipsecdoi_id_b *)new->v)->type = type;

        /* set ul_proto and port */
        /*
         * NOTE: we use both IPSEC_ULPROTO_ANY and IPSEC_PORT_ANY as wild card
         * because 0 means port number of 0.  Instead of 0, we use IPSEC_*_ANY.
         */
        ((struct ipsecdoi_id_b *)new->v)->proto_id =
                ul_proto == IPSEC_ULPROTO_ANY ? 0 : ul_proto;
        ((struct ipsecdoi_id_b *)new->v)->port =
                port == IPSEC_PORT_ANY ? 0 : port;
        memcpy(new->v + sizeof(struct ipsecdoi_id_b), sa, len1);

        /* set address */

        /* set prefix */
        if (len2) {
                u_char *p = (unsigned char *) new->v +
                        sizeof(struct ipsecdoi_id_b) + len1;
                u_int bits = prefixlen;

                while (bits >= 8) {
                        *p++ = 0xff;
                        bits -= 8;
                }

                if (bits > 0)
                        *p = ~((1 << (8 - bits)) - 1);
        }

        return new;
}

vchar_t *
ipsecdoi_sockrange2id(struct sockaddr *laddr, struct sockaddr *haddr,
    u_int ul_proto)
{
        vchar_t *new;
        int type, len1, len2;
        u_short port;

        if (laddr->sa_family != haddr->sa_family) {
            plog(LLV_ERROR, LOCATION, NULL, "Address family mismatch\n");
            return NULL;
        }

        switch (laddr->sa_family) {
        case AF_INET:
            type = IPSECDOI_ID_IPV4_ADDR_RANGE;
            len1 = sizeof(struct in_addr);
            len2 = sizeof(struct in_addr);
            break;
#ifdef INET6
        case AF_INET6:
                type = IPSECDOI_ID_IPV6_ADDR_RANGE;
                len1 = sizeof(struct in6_addr);
                len2 = sizeof(struct in6_addr);
                break;
#endif
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid family: %d.\n", laddr->sa_family);
                return NULL;
        }

        /* get ID buffer */
        new = vmalloc(sizeof(struct ipsecdoi_id_b) + len1 + len2);
        if (new == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get ID buffer.\n");
                return NULL;
        }

        memset(new->v, 0, new->l);
        /* set the part of header. */
        ((struct ipsecdoi_id_b *)new->v)->type = type;

        /* set ul_proto and port */
        /*
         * NOTE: we use both IPSEC_ULPROTO_ANY and IPSEC_PORT_ANY as wild card
         * because 0 means port number of 0.  Instead of 0, we use IPSEC_*_ANY.
         */
        ((struct ipsecdoi_id_b *)new->v)->proto_id =
                ul_proto == IPSEC_ULPROTO_ANY ? 0 : ul_proto;
        port = ((struct sockaddr_in *)(laddr))->sin_port;
        ((struct ipsecdoi_id_b *)new->v)->port =
                port == IPSEC_PORT_ANY ? 0 : port;
        memcpy(new->v + sizeof(struct ipsecdoi_id_b),
               (caddr_t)&((struct sockaddr_in *)(laddr))->sin_addr,
               len1);
        memcpy(new->v + sizeof(struct ipsecdoi_id_b) + len1,
               (caddr_t)&((struct sockaddr_in *)haddr)->sin_addr,
               len2);
        return new;
}


/*
 * create sockaddr structure from ID payload (buf).
 * buffers (saddr, prefixlen, ul_proto) must be allocated.
 * see, RFC2407 4.6.2.1
 */
int
ipsecdoi_id2sockaddr(vchar_t *buf, struct sockaddr *saddr,
    uint8_t *prefixlen, uint16_t *ul_proto)
{
        struct ipsecdoi_id_b *id_b = NULL;
        u_int plen = 0;

        if (buf == NULL)
                return ISAKMP_INTERNAL_ERROR;

        id_b = (struct ipsecdoi_id_b *)buf->v;

        /*
         * When a ID payload of subnet type with a IP address of full bit
         * masked, it has to be processed as host address.
         * e.g. below 2 type are same.
         *      type = ipv6 subnet, data = 2001::1/128
         *      type = ipv6 address, data = 2001::1
         */
        switch (id_b->type) {
        case IPSECDOI_ID_IPV4_ADDR:
        case IPSECDOI_ID_IPV4_ADDR_SUBNET:
#ifndef __linux__
                saddr->sa_len = sizeof(struct sockaddr_in);
#endif
                saddr->sa_family = AF_INET;
                ((struct sockaddr_in *)saddr)->sin_port =
                        (id_b->port == 0
                                ? IPSEC_PORT_ANY
                                : id_b->port);          /* see sockaddr2id() */
                memcpy(&((struct sockaddr_in *)saddr)->sin_addr,
                        buf->v + sizeof(*id_b), sizeof(struct in_addr));
                break;
#ifdef INET6
        case IPSECDOI_ID_IPV6_ADDR:
        case IPSECDOI_ID_IPV6_ADDR_SUBNET:
#ifndef __linux__
                saddr->sa_len = sizeof(struct sockaddr_in6);
#endif
                saddr->sa_family = AF_INET6;
                ((struct sockaddr_in6 *)saddr)->sin6_port =
                        (id_b->port == 0
                                ? IPSEC_PORT_ANY
                                : id_b->port);          /* see sockaddr2id() */
                memcpy(&((struct sockaddr_in6 *)saddr)->sin6_addr,
                        buf->v + sizeof(*id_b), sizeof(struct in6_addr));
                ((struct sockaddr_in6 *)saddr)->sin6_scope_id =
                        (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)saddr)->sin6_addr)
                                ? ((struct sockaddr_in6 *)id_b)->sin6_scope_id
                                : 0);

                break;
#endif
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "unsupported ID type %d\n", id_b->type);
                return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
        }

        /* get prefix length */
        switch (id_b->type) {
        case IPSECDOI_ID_IPV4_ADDR:
                plen = sizeof(struct in_addr) << 3;
                break;
#ifdef INET6
        case IPSECDOI_ID_IPV6_ADDR:
                plen = sizeof(struct in6_addr) << 3;
                break;
#endif
        case IPSECDOI_ID_IPV4_ADDR_SUBNET:
#ifdef INET6
        case IPSECDOI_ID_IPV6_ADDR_SUBNET:
#endif
            {
                u_char *p;
                u_int max;
                int alen = sizeof(struct in_addr);

                switch (id_b->type) {
                case IPSECDOI_ID_IPV4_ADDR_SUBNET:
                        alen = sizeof(struct in_addr);
                        break;
#ifdef INET6
                case IPSECDOI_ID_IPV6_ADDR_SUBNET:
                        alen = sizeof(struct in6_addr);
                        break;
#endif
                }

                /* sanity check */
                if (buf->l < alen)
                        return ISAKMP_INTERNAL_ERROR;

                /* get subnet mask length */
                plen = 0;
                max = alen <<3;

                p = (unsigned char *) buf->v
                        + sizeof(struct ipsecdoi_id_b)
                        + alen;

                for (; *p == 0xff; p++) {
                        plen += 8;
                        if (plen >= max)
                                break;
                }

                if (plen < max) {
                        u_int l = 0;
                        u_char b = ~(*p);

                        while (b) {
                                b >>= 1;
                                l++;
                        }

                        l = 8 - l;
                        plen += l;
                }
            }
                break;
        }

        *prefixlen = plen;
        *ul_proto = id_b->proto_id == 0
                                ? IPSEC_ULPROTO_ANY
                                : id_b->proto_id;       /* see sockaddr2id() */

        return 0;
}

/*
 * make printable string from ID payload except of general header.
 */
char *
ipsecdoi_id2str(const vchar_t *id)
{
#define BUFLEN 512
        char * ret = NULL;
        int len = 0;
        char *dat;
        static char buf[BUFLEN];
        struct ipsecdoi_id_b *id_b = (struct ipsecdoi_id_b *)id->v;
        union sockaddr_any saddr;
        u_int plen = 0;

        switch (id_b->type) {
        case IPSECDOI_ID_IPV4_ADDR:
        case IPSECDOI_ID_IPV4_ADDR_SUBNET:
        case IPSECDOI_ID_IPV4_ADDR_RANGE:

#ifndef __linux__
                saddr.sa.sa_len = sizeof(struct sockaddr_in);
#endif
                saddr.sa.sa_family = AF_INET;
                saddr.sin.sin_port = IPSEC_PORT_ANY;
                memcpy(&saddr.sin.sin_addr,
                        id->v + sizeof(*id_b), sizeof(struct in_addr));
                break;
#ifdef INET6
        case IPSECDOI_ID_IPV6_ADDR:
        case IPSECDOI_ID_IPV6_ADDR_SUBNET:
        case IPSECDOI_ID_IPV6_ADDR_RANGE:

#ifndef __linux__
                saddr.sa.sa_len = sizeof(struct sockaddr_in6);
#endif
                saddr.sa.sa_family = AF_INET6;
                saddr.sin6.sin6_port = IPSEC_PORT_ANY;
                memcpy(&saddr.sin6.sin6_addr,
                        id->v + sizeof(*id_b), sizeof(struct in6_addr));
                saddr.sin6.sin6_scope_id =
                        (IN6_IS_ADDR_LINKLOCAL(&saddr.sin6.sin6_addr)
                                ? ((struct sockaddr_in6 *)id_b)->sin6_scope_id
                                : 0);
                break;
#endif
        }

        switch (id_b->type) {
        case IPSECDOI_ID_IPV4_ADDR:
#ifdef INET6
        case IPSECDOI_ID_IPV6_ADDR:
#endif
                len = snprintf( buf, BUFLEN, "%s", saddrwop2str(&saddr.sa));
                break;

        case IPSECDOI_ID_IPV4_ADDR_SUBNET:
#ifdef INET6
        case IPSECDOI_ID_IPV6_ADDR_SUBNET:
#endif
            {
                u_char *p;
                u_int max;
                int alen = sizeof(struct in_addr);

                switch (id_b->type) {
                case IPSECDOI_ID_IPV4_ADDR_SUBNET:
                        alen = sizeof(struct in_addr);
                        break;
#ifdef INET6
                case IPSECDOI_ID_IPV6_ADDR_SUBNET:
                        alen = sizeof(struct in6_addr);
                        break;
#endif
                }

                /* sanity check */
                if (id->l < alen) {
                        len = 0;
                        break;
                }

                /* get subnet mask length */
                plen = 0;
                max = alen <<3;

                p = (unsigned char *) id->v
                        + sizeof(struct ipsecdoi_id_b)
                        + alen;

                for (; *p == 0xff; p++) {
                        plen += 8;
                        if (plen >= max)
                                break;
                }

                if (plen < max) {
                        u_int l = 0;
                        u_char b = ~(*p);

                        while (b) {
                                b >>= 1;
                                l++;
                        }

                        l = 8 - l;
                        plen += l;
                }

                len = snprintf( buf, BUFLEN, "%s/%i", saddrwop2str(&saddr.sa), plen);
            }
                break;

        case IPSECDOI_ID_IPV4_ADDR_RANGE:

                len = snprintf( buf, BUFLEN, "%s-", saddrwop2str(&saddr.sa));

#ifndef __linux__
                saddr.sa.sa_len = sizeof(struct sockaddr_in);
#endif
                saddr.sa.sa_family = AF_INET;
                saddr.sin.sin_port = IPSEC_PORT_ANY;
                memcpy(&saddr.sin.sin_addr,
                        id->v + sizeof(*id_b) + sizeof(struct in_addr),
                        sizeof(struct in_addr));

                len += snprintf(buf + len, BUFLEN - len, "%s", saddrwop2str(&saddr.sa));
                break;

#ifdef INET6
        case IPSECDOI_ID_IPV6_ADDR_RANGE:
                len = snprintf( buf, BUFLEN, "%s-", saddrwop2str(&saddr.sa));

#ifndef __linux__
                saddr.sa.sa_len = sizeof(struct sockaddr_in6);
#endif
                saddr.sa.sa_family = AF_INET6;
                saddr.sin6.sin6_port = IPSEC_PORT_ANY;
                memcpy(&saddr.sin6.sin6_addr,
                        id->v + sizeof(*id_b) + sizeof(struct in6_addr),
                        sizeof(struct in6_addr));
                saddr.sin6.sin6_scope_id =
                        (IN6_IS_ADDR_LINKLOCAL(&saddr.sin6.sin6_addr)
                                ? ((struct sockaddr_in6 *)id_b)->sin6_scope_id
                                : 0);

                len += snprintf(buf + len, BUFLEN - len, "%s", saddrwop2str(&saddr.sa));
                break;
#endif

        case IPSECDOI_ID_FQDN:
        case IPSECDOI_ID_USER_FQDN:
                len = id->l - sizeof(*id_b);
                if (len > BUFLEN)
                        len = BUFLEN;
                memcpy(buf, id->v + sizeof(*id_b), len);
                break;

        case IPSECDOI_ID_DER_ASN1_DN:
        case IPSECDOI_ID_DER_ASN1_GN:
        {
                X509_NAME *xn = NULL;

                dat = id->v + sizeof(*id_b);
                len = id->l - sizeof(*id_b);

                if (d2i_X509_NAME(&xn, (void*) &dat, len) != NULL) {
                        BIO *bio = BIO_new(BIO_s_mem());
                        X509_NAME_print_ex(bio, xn, 0, 0);
                        len = BIO_get_mem_data(bio, &dat);
                        if (len > BUFLEN)
                                len = BUFLEN;
                        memcpy(buf,dat,len);
                        BIO_free(bio);
                        X509_NAME_free(xn);
                } else {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "unable to extract asn1dn from id\n");

                        len = sprintf(buf, "<ASN1-DN>");
                }

                break;
        }

        /* currently unhandled id types */
        case IPSECDOI_ID_KEY_ID:
                len = sprintf( buf, "<KEY-ID>");
                break;

        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "unknown ID type %d\n", id_b->type);
        }

        if (!len)
                len = sprintf( buf, "<?>");

        ret = racoon_malloc(len+1);
        if (ret != NULL) {
                memcpy(ret,buf,len);
                ret[len]=0;
        }

        return ret;
}

/*
 * set IPsec data attributes into a proposal.
 * NOTE: MUST called per a transform.
 */
int
ipsecdoi_t2satrns(struct isakmp_pl_t *t, struct saprop *pp,
    struct saproto *pr, struct satrns *tr)
{
        struct isakmp_data *d, *prev;
        int flag, type;
        int error = -1;
        int life_t;
        int tlen;

        tr->trns_no = t->t_no;
        tr->trns_id = t->t_id;

        tlen = ntohs(t->h.len) - sizeof(*t);
        prev = (struct isakmp_data *)NULL;
        d = (struct isakmp_data *)(t + 1);

        /* default */
        life_t = IPSECDOI_ATTR_SA_LD_TYPE_DEFAULT;
        pp->lifetime = IPSECDOI_ATTR_SA_LD_SEC_DEFAULT;
        pp->lifebyte = 0;
        tr->authtype = IPSECDOI_ATTR_AUTH_NONE;

        while (tlen > 0) {

                type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
                flag = ntohs(d->type) & ISAKMP_GEN_MASK;

                plog(LLV_DEBUG, LOCATION, NULL,
                        "type=%s, flag=0x%04x, lorv=%s\n",
                        s_ipsecdoi_attr(type), flag,
                        s_ipsecdoi_attr_v(type, ntohs(d->lorv)));

                switch (type) {
                case IPSECDOI_ATTR_SA_LD_TYPE:
                {
                        int xtype = ntohs(d->lorv);
                        switch (xtype) {
                        case IPSECDOI_ATTR_SA_LD_TYPE_SEC:
                        case IPSECDOI_ATTR_SA_LD_TYPE_KB:
                                life_t = xtype;
                                break;
                        default:
                                plog(LLV_WARNING, LOCATION, NULL,
                                        "invalid life duration type. "
                                        "use default\n");
                                life_t = IPSECDOI_ATTR_SA_LD_TYPE_DEFAULT;
                                break;
                        }
                        break;
                }
                case IPSECDOI_ATTR_SA_LD:
                        if (prev == NULL
                         || (ntohs(prev->type) & ~ISAKMP_GEN_MASK) !=
                                        IPSECDOI_ATTR_SA_LD_TYPE) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                    "life duration must follow ltype\n");
                                break;
                        }

                    {
                        uint32_t tt;
                        vchar_t *ld_buf = NULL;

                        if (flag) {
                                /* i.e. ISAKMP_GEN_TV */
                                ld_buf = vmalloc(sizeof(d->lorv));
                                if (ld_buf == NULL) {
                                        plog(LLV_ERROR, LOCATION, NULL,
                                            "failed to get LD buffer.\n");
                                        goto end;
                                }
                                memcpy(ld_buf->v, &d->lorv, sizeof(d->lorv));
                        } else {
                                int len = ntohs(d->lorv);
                                /* i.e. ISAKMP_GEN_TLV */
                                ld_buf = vmalloc(len);
                                if (ld_buf == NULL) {
                                        plog(LLV_ERROR, LOCATION, NULL,
                                            "failed to get LD buffer.\n");
                                        goto end;
                                }
                                memcpy(ld_buf->v, d + 1, len);
                        }
                        switch (life_t) {
                        case IPSECDOI_ATTR_SA_LD_TYPE_SEC:
                                tt = ipsecdoi_set_ld(ld_buf);
                                vfree(ld_buf);
                                if (tt == 0) {
                                        plog(LLV_ERROR, LOCATION, NULL,
                                                "invalid life duration.\n");
                                        goto end;
                                }
                                /* lifetime must be equal in a proposal. */
                                if (pp->lifetime == IPSECDOI_ATTR_SA_LD_SEC_DEFAULT)
                                        pp->lifetime = tt;
                                else if (pp->lifetime != tt) {
                                        plog(LLV_ERROR, LOCATION, NULL,
                                                "lifetime mismatched "
                                                "in a proposal, "
                                                "prev:%ld curr:%u.\n",
                                                (long)pp->lifetime, tt);
                                        goto end;
                                }
                                break;
                        case IPSECDOI_ATTR_SA_LD_TYPE_KB:
                                tt = ipsecdoi_set_ld(ld_buf);
                                vfree(ld_buf);
                                if (tt == 0) {
                                        plog(LLV_ERROR, LOCATION, NULL,
                                                "invalid life duration.\n");
                                        goto end;
                                }
                                /* lifebyte must be equal in a proposal. */
                                if (pp->lifebyte == 0)
                                        pp->lifebyte = tt;
                                else if (pp->lifebyte != tt) {
                                        plog(LLV_ERROR, LOCATION, NULL,
                                                "lifebyte mismatched "
                                                "in a proposal, "
                                                "prev:%d curr:%u.\n",
                                                pp->lifebyte, tt);
                                        goto end;
                                }
                                break;
                        default:
                                vfree(ld_buf);
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "invalid life type: %d\n", life_t);
                                goto end;
                        }
                    }
                        break;

                case IPSECDOI_ATTR_GRP_DESC:
                        /*
                         * RFC2407: 4.5 IPSEC Security Association Attributes
                         *   Specifies the Oakley Group to be used in a PFS QM
                         *   negotiation.  For a list of supported values, see
                         *   Appendix A of [IKE].
                         */
                        if (pp->pfs_group == 0)
                                pp->pfs_group = (uint16_t)ntohs(d->lorv);
                        else if (pp->pfs_group != (uint16_t)ntohs(d->lorv)) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "pfs_group mismatched "
                                        "in a proposal.\n");
                                goto end;
                        }
                        break;

                case IPSECDOI_ATTR_ENC_MODE:
                        if (pr->encmode &&
                            pr->encmode != (uint16_t)ntohs(d->lorv)) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "multiple encmode exist "
                                        "in a transform.\n");
                                goto end;
                        }
                        pr->encmode = (uint16_t)ntohs(d->lorv);
                        break;

                case IPSECDOI_ATTR_AUTH:
                        if (tr->authtype != IPSECDOI_ATTR_AUTH_NONE) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "multiple authtype exist "
                                        "in a transform.\n");
                                goto end;
                        }
                        tr->authtype = (uint16_t)ntohs(d->lorv);
                        break;

                case IPSECDOI_ATTR_KEY_LENGTH:
                        if (pr->proto_id != IPSECDOI_PROTO_IPSEC_ESP) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "key length defined but not ESP");
                                goto end;
                        }
                        tr->encklen = ntohs(d->lorv);
                        break;
#ifdef HAVE_SECCTX
                case IPSECDOI_ATTR_SECCTX:
                {
                        int len = ntohs(d->lorv);
                        memcpy(&pp->sctx, d + 1, len);
                        pp->sctx.ctx_strlen = ntohs(pp->sctx.ctx_strlen);
                        break;
                }
#endif /* HAVE_SECCTX */
                case IPSECDOI_ATTR_KEY_ROUNDS:
                case IPSECDOI_ATTR_COMP_DICT_SIZE:
                case IPSECDOI_ATTR_COMP_PRIVALG:
                default:
                        break;
                }

                prev = d;
                if (flag) {
                        tlen -= sizeof(*d);
                        d = (struct isakmp_data *)((char *)d + sizeof(*d));
                } else {
                        tlen -= (sizeof(*d) + ntohs(d->lorv));
                        d = (struct isakmp_data *)((caddr_t)d + sizeof(*d) + ntohs(d->lorv));
                }
        }

        error = 0;
end:
        return error;
}

int
ipsecdoi_authalg2trnsid(int alg)
{
        switch (alg) {
        case IPSECDOI_ATTR_AUTH_HMAC_MD5:
                return IPSECDOI_AH_MD5;
        case IPSECDOI_ATTR_AUTH_HMAC_SHA1:
                return IPSECDOI_AH_SHA;
        case IPSECDOI_ATTR_AUTH_HMAC_SHA2_256:
                return IPSECDOI_AH_SHA256;
        case IPSECDOI_ATTR_AUTH_HMAC_SHA2_384:
                return IPSECDOI_AH_SHA384;
        case IPSECDOI_ATTR_AUTH_HMAC_SHA2_512:
                return IPSECDOI_AH_SHA512;
        case IPSECDOI_ATTR_AUTH_DES_MAC:
                return IPSECDOI_AH_DES;
        case IPSECDOI_ATTR_AUTH_KPDK:
                return IPSECDOI_AH_MD5; /* XXX */
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid authentication algorithm:%d\n", alg);
        }
        return -1;
}

static int rm_idtype2doi[] = {
        255,                            /* IDTYPE_UNDEFINED, 0 */
        IPSECDOI_ID_FQDN,               /* IDTYPE_FQDN, 1 */
        IPSECDOI_ID_USER_FQDN,          /* IDTYPE_USERFQDN, 2 */
        IPSECDOI_ID_KEY_ID,             /* IDTYPE_KEYID, 3 */
        255,    /*                         IDTYPE_ADDRESS, 4
                 * it expands into 4 types by another function. */
        IPSECDOI_ID_DER_ASN1_DN,        /* IDTYPE_ASN1DN, 5 */
};

/*
 * convert idtype to DOI value.
 * OUT  255  : NG
 *      other: converted.
 */
int
idtype2doi(int idtype)
{
        if (ARRAYLEN(rm_idtype2doi) > idtype)
                return rm_idtype2doi[idtype];
        return 255;
}

int
doi2idtype(int doi)
{
        switch(doi) {
        case IPSECDOI_ID_FQDN:
                return(IDTYPE_FQDN);
        case IPSECDOI_ID_USER_FQDN:
                return(IDTYPE_USERFQDN);
        case IPSECDOI_ID_KEY_ID:
                return(IDTYPE_KEYID);
        case IPSECDOI_ID_DER_ASN1_DN:
                return(IDTYPE_ASN1DN);
        case IPSECDOI_ID_IPV4_ADDR:
        case IPSECDOI_ID_IPV4_ADDR_SUBNET:
        case IPSECDOI_ID_IPV6_ADDR:
        case IPSECDOI_ID_IPV6_ADDR_SUBNET:
                return(IDTYPE_ADDRESS);
        default:
                plog(LLV_WARNING, LOCATION, NULL,
                        "Inproper idtype:%s in this function.\n",
                        s_ipsecdoi_ident(doi));
                return(IDTYPE_ADDRESS); /* XXX */
        }
        /*NOTREACHED*/
}