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

/* Id: oakley.c,v 1.32 2006/05/26 12:19:46 manubsd 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> /* XXX for subjectaltname */
#include <netinet/in.h> /* XXX for subjectaltname */

#include <openssl/pkcs7.h>
#include <openssl/x509.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.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
#ifdef ENABLE_HYBRID
#include <resolv.h>
#endif

#include "var.h"
#include "misc.h"
#include "vmbuf.h"
#include "str2val.h"
#include "plog.h"
#include "debug.h"

#include "isakmp_var.h"
#include "isakmp.h"
#ifdef ENABLE_HYBRID
#include "isakmp_xauth.h"
#include "isakmp_cfg.h" 
#endif                
#include "oakley.h"
#include "admin.h"
#include "privsep.h"
#include "localconf.h"
#include "remoteconf.h"
#include "policy.h"
#include "handler.h"
#include "ipsec_doi.h"
#include "algorithm.h"
#include "dhgroup.h"
#include "sainfo.h"
#include "proposal.h"
#include "crypto_openssl.h"
#include "dnssec.h"
#include "sockmisc.h"
#include "strnames.h"
#include "gcmalloc.h"
#include "rsalist.h"

#ifdef HAVE_GSSAPI
#include "gssapi.h"
#endif

#define OUTBOUND_SA     0
#define INBOUND_SA      1

#define INITDHVAL(a, s, d, t)                                                  \
do {                                                                           \
        vchar_t buf;                                                           \
        buf.v = str2val((s), 16, &buf.l);                                      \
        memset(&a, 0, sizeof(struct dhgroup));                                 \
        a.type = (t);                                                          \
        a.prime = vdup(&buf);                                                  \
        a.gen1 = 2;                                                            \
        a.gen2 = 0;                                                            \
        racoon_free(buf.v);                                                    \
} while(0)

struct dhgroup dh_modp768;
struct dhgroup dh_modp1024;
struct dhgroup dh_modp1536;
struct dhgroup dh_modp2048;
struct dhgroup dh_modp3072;
struct dhgroup dh_modp4096;
struct dhgroup dh_modp6144;
struct dhgroup dh_modp8192;


static int oakley_check_dh_pub(vchar_t *, vchar_t **);
static int oakley_compute_keymat_x(struct ph2handle *, int, int);
static int oakley_check_certid(struct ph1handle *iph1);
static int check_typeofcertname(int, int);
static int oakley_padlen(int, int);
static int get_plainrsa_fromlocal(struct ph1handle *, int);

int oakley_get_certtype(vchar_t *cert)
{
        if (cert == NULL)
                return ISAKMP_CERT_NONE;

        return cert->v[0];
}

static vchar_t *
dump_isakmp_payload(struct isakmp_gen *gen)
{
        vchar_t p;

        if (ntohs(gen->len) <= sizeof(*gen)) {
                plog(LLV_ERROR, LOCATION, NULL,
                     "Len is too small !!.\n");
                return NULL;
        }

        p.v = (caddr_t) (gen + 1);
        p.l = ntohs(gen->len) - sizeof(*gen);

        return vdup(&p);
}

static vchar_t *
dump_x509(X509 *cert)
{
        vchar_t *pl;
        u_char *bp;
        int len;

        len = i2d_X509(cert, NULL);

        pl = vmalloc(len + 1);
        if (pl == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                     "Failed to copy CERT from packet.\n");
                return NULL;
        }

        pl->v[0] = ISAKMP_CERT_X509SIGN;
        bp = (u_char *) &pl->v[1];
        i2d_X509(cert, &bp);

        return pl;
}



int
oakley_get_defaultlifetime()
{
        return OAKLEY_ATTR_SA_LD_SEC_DEFAULT;
}

int
oakley_dhinit()
{
        /* set DH MODP */
        INITDHVAL(dh_modp768, OAKLEY_PRIME_MODP768,
                OAKLEY_ATTR_GRP_DESC_MODP768, OAKLEY_ATTR_GRP_TYPE_MODP);
        INITDHVAL(dh_modp1024, OAKLEY_PRIME_MODP1024,
                OAKLEY_ATTR_GRP_DESC_MODP1024, OAKLEY_ATTR_GRP_TYPE_MODP);
        INITDHVAL(dh_modp1536, OAKLEY_PRIME_MODP1536,
                OAKLEY_ATTR_GRP_DESC_MODP1536, OAKLEY_ATTR_GRP_TYPE_MODP);
        INITDHVAL(dh_modp2048, OAKLEY_PRIME_MODP2048,
                OAKLEY_ATTR_GRP_DESC_MODP2048, OAKLEY_ATTR_GRP_TYPE_MODP);
        INITDHVAL(dh_modp3072, OAKLEY_PRIME_MODP3072,
                OAKLEY_ATTR_GRP_DESC_MODP3072, OAKLEY_ATTR_GRP_TYPE_MODP);
        INITDHVAL(dh_modp4096, OAKLEY_PRIME_MODP4096,
                OAKLEY_ATTR_GRP_DESC_MODP4096, OAKLEY_ATTR_GRP_TYPE_MODP);
        INITDHVAL(dh_modp6144, OAKLEY_PRIME_MODP6144,
                OAKLEY_ATTR_GRP_DESC_MODP6144, OAKLEY_ATTR_GRP_TYPE_MODP);
        INITDHVAL(dh_modp8192, OAKLEY_PRIME_MODP8192,
                OAKLEY_ATTR_GRP_DESC_MODP8192, OAKLEY_ATTR_GRP_TYPE_MODP);

        return 0;
}

void
oakley_dhgrp_free(struct dhgroup *dhgrp)
{
        if (dhgrp->prime)
                vfree(dhgrp->prime);
        if (dhgrp->curve_a)
                vfree(dhgrp->curve_a);
        if (dhgrp->curve_b)
                vfree(dhgrp->curve_b);
        if (dhgrp->order)
                vfree(dhgrp->order);
        racoon_free(dhgrp);
}

/*
 * RFC2409 5
 * The length of the Diffie-Hellman public value MUST be equal to the
 * length of the prime modulus over which the exponentiation was
 * performed, prepending zero bits to the value if necessary.
 */
static int
oakley_check_dh_pub(vchar_t *prime, vchar_t **pub0)
{
        vchar_t *tmp;
        vchar_t *pub = *pub0;

        if (prime->l == pub->l)
                return 0;

        if (prime->l < pub->l) {
                /* what should i do ? */
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid public information was generated.\n");
                return -1;
        }

        /* prime->l > pub->l */
        tmp = vmalloc(prime->l);
        if (tmp == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get DH buffer.\n");
                return -1;
        }
        memcpy(tmp->v + prime->l - pub->l, pub->v, pub->l);

        vfree(*pub0);
        *pub0 = tmp;

        return 0;
}

/*
 * compute sharing secret of DH
 * IN:  *dh, *pub, *priv, *pub_p
 * OUT: **gxy
 */
int
oakley_dh_compute(const struct dhgroup *dh, vchar_t *pub, vchar_t *priv,
    vchar_t *pub_p, vchar_t **gxy)
{
#ifdef ENABLE_STATS
        struct timeval start, end;
#endif
        if ((*gxy = vmalloc(dh->prime->l)) == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get DH buffer.\n");
                return -1;
        }

#ifdef ENABLE_STATS
        gettimeofday(&start, NULL);
#endif
        switch (dh->type) {
        case OAKLEY_ATTR_GRP_TYPE_MODP:
                if (eay_dh_compute(dh->prime, dh->gen1, pub, priv, pub_p, gxy) < 0) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "failed to compute dh value.\n");
                        return -1;
                }
                break;
        case OAKLEY_ATTR_GRP_TYPE_ECP:
        case OAKLEY_ATTR_GRP_TYPE_EC2N:
                plog(LLV_ERROR, LOCATION, NULL,
                        "dh type %d isn't supported.\n", dh->type);
                return -1;
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid dh type %d.\n", dh->type);
                return -1;
        }

#ifdef ENABLE_STATS
        gettimeofday(&end, NULL);
        syslog(LOG_NOTICE, "%s(%s%zu): %8.6f", __func__,
                s_attr_isakmp_group(dh->type), dh->prime->l << 3,
                timedelta(&start, &end));
#endif

        plog(LLV_DEBUG, LOCATION, NULL, "compute DH's shared.\n");
        plogdump(LLV_DEBUG, (*gxy)->v, (*gxy)->l);

        return 0;
}

/*
 * generate values of DH
 * IN:  *dh
 * OUT: **pub, **priv
 */
int
oakley_dh_generate(const struct dhgroup *dh, vchar_t **pub, vchar_t **priv)
{
#ifdef ENABLE_STATS
        struct timeval start, end;
        gettimeofday(&start, NULL);
#endif
        switch (dh->type) {
        case OAKLEY_ATTR_GRP_TYPE_MODP:
                if (eay_dh_generate(dh->prime, dh->gen1, dh->gen2, pub, priv) < 0) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "failed to compute dh value.\n");
                        return -1;
                }
                break;

        case OAKLEY_ATTR_GRP_TYPE_ECP:
        case OAKLEY_ATTR_GRP_TYPE_EC2N:
                plog(LLV_ERROR, LOCATION, NULL,
                        "dh type %d isn't supported.\n", dh->type);
                return -1;
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid dh type %d.\n", dh->type);
                return -1;
        }

#ifdef ENABLE_STATS
        gettimeofday(&end, NULL);
        syslog(LOG_NOTICE, "%s(%s%zu): %8.6f", __func__,
                s_attr_isakmp_group(dh->type), dh->prime->l << 3,
                timedelta(&start, &end));
#endif

        if (oakley_check_dh_pub(dh->prime, pub) != 0)
                return -1;

        plog(LLV_DEBUG, LOCATION, NULL, "compute DH's private.\n");
        plogdump(LLV_DEBUG, (*priv)->v, (*priv)->l);
        plog(LLV_DEBUG, LOCATION, NULL, "compute DH's public.\n");
        plogdump(LLV_DEBUG, (*pub)->v, (*pub)->l);

        return 0;
}

/*
 * copy pre-defined dhgroup values.
 */
int
oakley_setdhgroup(int group, struct dhgroup **dhgrp)
{
        struct dhgroup *g;

        *dhgrp = NULL;  /* just make sure, initialize */

        g = alg_oakley_dhdef_group(group);
        if (g == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid DH parameter grp=%d.\n", group);
                return -1;
        }

        if (!g->type || !g->prime || !g->gen1) {
                /* unsuported */
                plog(LLV_ERROR, LOCATION, NULL,
                        "unsupported DH parameters grp=%d.\n", group);
                return -1;
        }

        *dhgrp = racoon_calloc(1, sizeof(struct dhgroup));
        if (*dhgrp == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get DH buffer.\n");
                return 0;
        }

        /* set defined dh values */
        memcpy(*dhgrp, g, sizeof(*g));
        (*dhgrp)->prime = vdup(g->prime);

        return 0;
}

/*
 * PRF
 *
 * NOTE: we do not support prf with different input/output bitwidth,
 * so we do not implement RFC2409 Appendix B (DOORAK-MAC example) in
 * oakley_compute_keymat().  If you add support for such prf function,
 * modify oakley_compute_keymat() accordingly.
 */
vchar_t *
oakley_prf(vchar_t *key, vchar_t *buf, struct ph1handle *iph1)
{
        vchar_t *res = NULL;
        int type;

        if (iph1->approval == NULL) {
                /*
                 * it's before negotiating hash algorithm.
                 * We use md5 as default.
                 */
                type = OAKLEY_ATTR_HASH_ALG_MD5;
        } else
                type = iph1->approval->hashtype;

        res = alg_oakley_hmacdef_one(type, key, buf);
        if (res == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid hmac algorithm %d.\n", type);
                return NULL;
        }

        return res;
}

/*
 * hash
 */
vchar_t *
oakley_hash(vchar_t *buf, struct ph1handle *iph1)
{
        vchar_t *res = NULL;
        int type;

        if (iph1->approval == NULL) {
                /*
                 * it's before negotiating hash algorithm.
                 * We use md5 as default.
                 */
                type = OAKLEY_ATTR_HASH_ALG_MD5;
        } else
                type = iph1->approval->hashtype;

        res = alg_oakley_hashdef_one(type, buf);
        if (res == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid hash algorithm %d.\n", type);
                return NULL;
        }

        return res;
}

/*
 * compute KEYMAT
 *   see seciton 5.5 Phase 2 - Quick Mode in isakmp-oakley-05.
 */
int
oakley_compute_keymat(struct ph2handle *iph2, int side)
{
        int error = -1;

        /* compute sharing secret of DH when PFS */
        if (iph2->approval->pfs_group && iph2->dhpub_p) {
                if (oakley_dh_compute(iph2->pfsgrp, iph2->dhpub,
                                iph2->dhpriv, iph2->dhpub_p, &iph2->dhgxy) < 0)
                        goto end;
        }

        /* compute keymat */
        if (oakley_compute_keymat_x(iph2, side, INBOUND_SA) < 0
         || oakley_compute_keymat_x(iph2, side, OUTBOUND_SA) < 0)
                goto end;

        plog(LLV_DEBUG, LOCATION, NULL, "KEYMAT computed.\n");

        error = 0;

end:
        return error;
}

/*
 * compute KEYMAT.
 * KEYMAT = prf(SKEYID_d, protocol | SPI | Ni_b | Nr_b).
 * If PFS is desired and KE payloads were exchanged,
 *   KEYMAT = prf(SKEYID_d, g(qm)^xy | protocol | SPI | Ni_b | Nr_b)
 *
 * NOTE: we do not support prf with different input/output bitwidth,
 * so we do not implement RFC2409 Appendix B (DOORAK-MAC example).
 */
static int
oakley_compute_keymat_x(struct ph2handle *iph2, int side, int sa_dir)
{
        vchar_t *buf = NULL, *res = NULL, *bp;
        char *p;
        size_t len;
        int error = -1;
        int pfs = 0;
        int dupkeymat;  /* generate K[1-dupkeymat] */
        struct saproto *pr;
        struct satrns *tr;
        int encklen, authklen, sl;

        pfs = ((iph2->approval->pfs_group && iph2->dhgxy) ? 1 : 0);
        
        len = pfs ? iph2->dhgxy->l : 0;
        len += (1
                + sizeof(uint32_t)      /* XXX SPI size */
                + iph2->nonce->l
                + iph2->nonce_p->l);
        buf = vmalloc(len);
        if (buf == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get keymat buffer.\n");
                goto end;
        }

        for (pr = iph2->approval->head; pr != NULL; pr = pr->next) {
                p = buf->v;

                /* if PFS */
                if (pfs) {
                        memcpy(p, iph2->dhgxy->v, iph2->dhgxy->l);
                        p += iph2->dhgxy->l;
                }

                p[0] = pr->proto_id;
                p += 1;

                memcpy(p, (sa_dir == INBOUND_SA ? &pr->spi : &pr->spi_p),
                        sizeof(pr->spi));
                p += sizeof(pr->spi);

                bp = (side == INITIATOR ? iph2->nonce : iph2->nonce_p);
                memcpy(p, bp->v, bp->l);
                p += bp->l;

                bp = (side == INITIATOR ? iph2->nonce_p : iph2->nonce);
                memcpy(p, bp->v, bp->l);
                p += bp->l;

                /* compute IV */
                plog(LLV_DEBUG, LOCATION, NULL, "KEYMAT compute with\n");
                plogdump(LLV_DEBUG, buf->v, buf->l);

                /* res = K1 */
                res = oakley_prf(iph2->ph1->skeyid_d, buf, iph2->ph1);
                if (res == NULL)
                        goto end;

                /* compute key length needed */
                encklen = authklen = 0;
                switch (pr->proto_id) {
                case IPSECDOI_PROTO_IPSEC_ESP:
                        for (tr = pr->head; tr; tr = tr->next) {
                                sl = alg_ipsec_encdef_keylen(tr->trns_id,
                                    tr->encklen);
                                if (sl > encklen)
                                        encklen = sl;

                                sl = alg_ipsec_hmacdef_hashlen(tr->authtype);
                                if (sl > authklen)
                                        authklen = sl;
                        }
                        break;
                case IPSECDOI_PROTO_IPSEC_AH:
                        for (tr = pr->head; tr; tr = tr->next) {
                                sl = alg_ipsec_hmacdef_hashlen(tr->trns_id);
                                if (sl > authklen)
                                        authklen = sl;
                        }
                        break;
                default:
                        break;
                }
                plog(LLV_DEBUG, LOCATION, NULL, "encklen=%d authklen=%d\n",
                        encklen, authklen);

                dupkeymat = (encklen + authklen) / 8 / res->l;
                dupkeymat += 2; /* safety mergin */
                if (dupkeymat < 3)
                        dupkeymat = 3;
                plog(LLV_DEBUG, LOCATION, NULL,
                        "generating %zu bits of key (dupkeymat=%d)\n",
                        dupkeymat * 8 * res->l, dupkeymat);
                if (0 < --dupkeymat) {
                        vchar_t *prev = res;    /* K(n-1) */
                        vchar_t *seed = NULL;   /* seed for Kn */
                        size_t l;

                        /*
                         * generating long key (isakmp-oakley-08 5.5)
                         *   KEYMAT = K1 | K2 | K3 | ...
                         * where
                         *   src = [ g(qm)^xy | ] protocol | SPI | Ni_b | Nr_b
                         *   K1 = prf(SKEYID_d, src)
                         *   K2 = prf(SKEYID_d, K1 | src)
                         *   K3 = prf(SKEYID_d, K2 | src)
                         *   Kn = prf(SKEYID_d, K(n-1) | src)
                         */
                        plog(LLV_DEBUG, LOCATION, NULL,
                                "generating K1...K%d for KEYMAT.\n",
                                dupkeymat + 1);

                        seed = vmalloc(prev->l + buf->l);
                        if (seed == NULL) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "failed to get keymat buffer.\n");
                                if (prev && prev != res)
                                        vfree(prev);
                                goto end;
                        }

                        while (dupkeymat--) {
                                vchar_t *this = NULL;   /* Kn */
                                int update_prev;

                                memcpy(seed->v, prev->v, prev->l);
                                memcpy(seed->v + prev->l, buf->v, buf->l);
                                this = oakley_prf(iph2->ph1->skeyid_d, seed,
                                                        iph2->ph1);
                                if (!this) {
                                        plog(LLV_ERROR, LOCATION, NULL,
                                                "oakley_prf memory overflow\n");
                                        if (prev && prev != res)
                                                vfree(prev);
                                        vfree(this);
                                        vfree(seed);
                                        goto end;
                                }

                                update_prev = (prev && prev == res) ? 1 : 0;

                                l = res->l;
                                res = vrealloc(res, l + this->l);

                                if (update_prev)
                                        prev = res;

                                if (res == NULL) {
                                        plog(LLV_ERROR, LOCATION, NULL,
                                                "failed to get keymat buffer.\n");
                                        if (prev && prev != res)
                                                vfree(prev);
                                        vfree(this);
                                        vfree(seed);
                                        goto end;
                                }
                                memcpy(res->v + l, this->v, this->l);

                                if (prev && prev != res)
                                        vfree(prev);
                                prev = this;
                                this = NULL;
                        }

                        if (prev && prev != res)
                                vfree(prev);
                        vfree(seed);
                }

                plogdump(LLV_DEBUG, res->v, res->l);

                if (sa_dir == INBOUND_SA)
                        pr->keymat = res;
                else
                        pr->keymat_p = res;
                res = NULL;
        }

        error = 0;

end:
        if (error) {
                for (pr = iph2->approval->head; pr != NULL; pr = pr->next) {
                        if (pr->keymat) {
                                vfree(pr->keymat);
                                pr->keymat = NULL;
                        }
                        if (pr->keymat_p) {
                                vfree(pr->keymat_p);
                                pr->keymat_p = NULL;
                        }
                }
        }

        if (buf != NULL)
                vfree(buf);
        if (res)
                vfree(res);

        return error;
}

#if notyet
/*
 * NOTE: Must terminate by NULL.
 */
vchar_t *
oakley_compute_hashx(struct ph1handle *iph1, ...)
{
        vchar_t *buf, *res;
        vchar_t *s;
        caddr_t p;
        int len;

        va_list ap;

        /* get buffer length */
        va_start(ap, iph1);
        len = 0;
        while ((s = va_arg(ap, vchar_t *)) != NULL) {
                len += s->l
        }
        va_end(ap);

        buf = vmalloc(len);
        if (buf == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get hash buffer\n");
                return NULL;
        }

        /* set buffer */
        va_start(ap, iph1);
        p = buf->v;
        while ((s = va_arg(ap, char *)) != NULL) {
                memcpy(p, s->v, s->l);
                p += s->l;
        }
        va_end(ap);

        plog(LLV_DEBUG, LOCATION, NULL, "HASH with: \n");
        plogdump(LLV_DEBUG, buf->v, buf->l);

        /* compute HASH */
        res = oakley_prf(iph1->skeyid_a, buf, iph1);
        vfree(buf);
        if (res == NULL)
                return NULL;

        plog(LLV_DEBUG, LOCATION, NULL, "HASH computed:\n");
        plogdump(LLV_DEBUG, res->v, res->l);

        return res;
}
#endif

/*
 * compute HASH(3) prf(SKEYID_a, 0 | M-ID | Ni_b | Nr_b)
 *   see seciton 5.5 Phase 2 - Quick Mode in isakmp-oakley-05.
 */
vchar_t *
oakley_compute_hash3(struct ph1handle *iph1, uint32_t msgid, vchar_t *body)
{
        vchar_t *buf = 0, *res = 0;
        size_t len;

        /* create buffer */
        len = 1 + sizeof(uint32_t) + body->l;
        buf = vmalloc(len);
        if (buf == NULL) {
                plog(LLV_DEBUG, LOCATION, NULL,
                        "failed to get hash buffer\n");
                goto end;
        }

        buf->v[0] = 0;

        memcpy(buf->v + 1, (char *)&msgid, sizeof(msgid));

        memcpy(buf->v + 1 + sizeof(uint32_t), body->v, body->l);

        plog(LLV_DEBUG, LOCATION, NULL, "HASH with: \n");
        plogdump(LLV_DEBUG, buf->v, buf->l);

        /* compute HASH */
        res = oakley_prf(iph1->skeyid_a, buf, iph1);
        if (res == NULL)
                goto end;

        plog(LLV_DEBUG, LOCATION, NULL, "HASH computed:\n");
        plogdump(LLV_DEBUG, res->v, res->l);

end:
        if (buf != NULL)
                vfree(buf);
        return res;
}

/*
 * compute HASH type of prf(SKEYID_a, M-ID | buffer)
 *      e.g.
 *      for quick mode HASH(1):
 *              prf(SKEYID_a, M-ID | SA | Ni [ | KE ] [ | IDci | IDcr ])
 *      for quick mode HASH(2):
 *              prf(SKEYID_a, M-ID | Ni_b | SA | Nr [ | KE ] [ | IDci | IDcr ])
 *      for Informational exchange:
 *              prf(SKEYID_a, M-ID | N/D)
 */
vchar_t *
oakley_compute_hash1(struct ph1handle *iph1, uint32_t msgid, vchar_t *body)
{
        vchar_t *buf = NULL, *res = NULL;
        char *p;
        size_t len;

        /* create buffer */
        len = sizeof(uint32_t) + body->l;
        buf = vmalloc(len);
        if (buf == NULL) {
                plog(LLV_DEBUG, LOCATION, NULL,
                        "failed to get hash buffer\n");
                goto end;
        }

        p = buf->v;

        memcpy(buf->v, (char *)&msgid, sizeof(msgid));
        p += sizeof(uint32_t);

        memcpy(p, body->v, body->l);

        plog(LLV_DEBUG, LOCATION, NULL, "HASH with:\n");
        plogdump(LLV_DEBUG, buf->v, buf->l);

        /* compute HASH */
        res = oakley_prf(iph1->skeyid_a, buf, iph1);
        if (res == NULL)
                goto end;

        plog(LLV_DEBUG, LOCATION, NULL, "HASH computed:\n");
        plogdump(LLV_DEBUG, res->v, res->l);

end:
        if (buf != NULL)
                vfree(buf);
        return res;
}

/*
 * compute phase1 HASH
 * main/aggressive
 *   I-digest = prf(SKEYID, g^i | g^r | CKY-I | CKY-R | SAi_b | ID_i1_b)
 *   R-digest = prf(SKEYID, g^r | g^i | CKY-R | CKY-I | SAi_b | ID_r1_b)
 * for gssapi, also include all GSS tokens, and call gss_wrap on the result
 */
vchar_t *
oakley_ph1hash_common(struct ph1handle *iph1, int sw)
{
        vchar_t *buf = NULL, *res = NULL, *bp;
        char *p, *bp2;
        size_t len;
        int bl;
#ifdef HAVE_GSSAPI
        vchar_t *gsstokens = NULL;
#endif

        /* create buffer */
        len = iph1->dhpub->l
                + iph1->dhpub_p->l
                + sizeof(cookie_t) * 2
                + iph1->sa->l
                + (sw == GENERATE ? iph1->id->l : iph1->id_p->l);

#ifdef HAVE_GSSAPI
        if (iph1->approval->authmethod == OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB) {
                if (iph1->gi_i != NULL && iph1->gi_r != NULL) {
                        bp = (sw == GENERATE ? iph1->gi_i : iph1->gi_r);
                        len += bp->l;
                }
                if (sw == GENERATE)
                        gssapi_get_itokens(iph1, &gsstokens);
                else
                        gssapi_get_rtokens(iph1, &gsstokens);
                if (gsstokens == NULL)
                        return NULL;
                len += gsstokens->l;
        }
#endif

        buf = vmalloc(len);
        if (buf == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get hash buffer\n");
                goto end;
        }

        p = buf->v;

        bp = (sw == GENERATE ? iph1->dhpub : iph1->dhpub_p);
        memcpy(p, bp->v, bp->l);
        p += bp->l;

        bp = (sw == GENERATE ? iph1->dhpub_p : iph1->dhpub);
        memcpy(p, bp->v, bp->l);
        p += bp->l;

        if (iph1->side == INITIATOR)
                bp2 = (sw == GENERATE ?
                      (char *)&iph1->index.i_ck : (char *)&iph1->index.r_ck);
        else
                bp2 = (sw == GENERATE ?
                      (char *)&iph1->index.r_ck : (char *)&iph1->index.i_ck);
        bl = sizeof(cookie_t);
        memcpy(p, bp2, bl);
        p += bl;

        if (iph1->side == INITIATOR)
                bp2 = (sw == GENERATE ?
                      (char *)&iph1->index.r_ck : (char *)&iph1->index.i_ck);
        else
                bp2 = (sw == GENERATE ?
                      (char *)&iph1->index.i_ck : (char *)&iph1->index.r_ck);
        bl = sizeof(cookie_t);
        memcpy(p, bp2, bl);
        p += bl;

        bp = iph1->sa;
        memcpy(p, bp->v, bp->l);
        p += bp->l;

        bp = (sw == GENERATE ? iph1->id : iph1->id_p);
        memcpy(p, bp->v, bp->l);
        p += bp->l;

#ifdef HAVE_GSSAPI
        if (iph1->approval->authmethod == OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB) {
                if (iph1->gi_i != NULL && iph1->gi_r != NULL) {
                        bp = (sw == GENERATE ? iph1->gi_i : iph1->gi_r);
                        memcpy(p, bp->v, bp->l);
                        p += bp->l;
                }
                memcpy(p, gsstokens->v, gsstokens->l);
                p += gsstokens->l;
        }
#endif

        plog(LLV_DEBUG, LOCATION, NULL, "HASH with:\n");
        plogdump(LLV_DEBUG, buf->v, buf->l);

        /* compute HASH */
        res = oakley_prf(iph1->skeyid, buf, iph1);
        if (res == NULL)
                goto end;

        plog(LLV_DEBUG, LOCATION, NULL, "HASH (%s) computed:\n",
                iph1->side == INITIATOR ? "init" : "resp");
        plogdump(LLV_DEBUG, res->v, res->l);

end:
        if (buf != NULL)
                vfree(buf);
#ifdef HAVE_GSSAPI
        if (gsstokens != NULL)
                vfree(gsstokens);
#endif
        return res;
}

/*
 * compute HASH_I on base mode.
 * base:psk,rsa
 *   HASH_I = prf(SKEYID, g^xi | CKY-I | CKY-R | SAi_b | IDii_b)
 * base:sig
 *   HASH_I = prf(hash(Ni_b | Nr_b), g^xi | CKY-I | CKY-R | SAi_b | IDii_b)
 */
vchar_t *
oakley_ph1hash_base_i(struct ph1handle *iph1, int sw)
{
        vchar_t *buf = NULL, *res = NULL, *bp;
        vchar_t *hashkey = NULL;
        vchar_t *hash = NULL;   /* for signature mode */
        char *p;
        size_t len;

        /* sanity check */
        if (iph1->etype != ISAKMP_ETYPE_BASE) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid etype for this hash function\n");
                return NULL;
        }

        switch (iph1->approval->authmethod) {
        case OAKLEY_ATTR_AUTH_METHOD_PSKEY:
        case OAKLEY_ATTR_AUTH_METHOD_RSAENC:
        case OAKLEY_ATTR_AUTH_METHOD_RSAREV:
#ifdef ENABLE_HYBRID
        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:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R:
#endif
                if (iph1->skeyid == NULL) {
                        plog(LLV_ERROR, LOCATION, NULL, "no SKEYID found.\n");
                        return NULL;
                }
                hashkey = iph1->skeyid;
                break;

        case OAKLEY_ATTR_AUTH_METHOD_DSSSIG:
        case OAKLEY_ATTR_AUTH_METHOD_RSASIG:
#ifdef HAVE_GSSAPI
        case OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB:
#endif
#ifdef ENABLE_HYBRID
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I:
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R:
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I:
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R:
#endif
                /* make hash for seed */
                len = iph1->nonce->l + iph1->nonce_p->l;
                buf = vmalloc(len);
                if (buf == NULL) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "failed to get hash buffer\n");
                        goto end;
                }
                p = buf->v;

                bp = (sw == GENERATE ? iph1->nonce_p : iph1->nonce);
                memcpy(p, bp->v, bp->l);
                p += bp->l;

                bp = (sw == GENERATE ? iph1->nonce : iph1->nonce_p);
                memcpy(p, bp->v, bp->l);
                p += bp->l;

                hash = oakley_hash(buf, iph1);
                if (hash == NULL)
                        goto end;
                vfree(buf);
                buf = NULL;

                hashkey = hash;
                break;

        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "not supported authentication method %d\n",
                        iph1->approval->authmethod);
                return NULL;

        }

        len = (sw == GENERATE ? iph1->dhpub->l : iph1->dhpub_p->l)
                + sizeof(cookie_t) * 2
                + iph1->sa->l
                + (sw == GENERATE ? iph1->id->l : iph1->id_p->l);
        buf = vmalloc(len);
        if (buf == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get hash buffer\n");
                goto end;
        }
        p = buf->v;

        bp = (sw == GENERATE ? iph1->dhpub : iph1->dhpub_p);
        memcpy(p, bp->v, bp->l);
        p += bp->l;

        memcpy(p, &iph1->index.i_ck, sizeof(cookie_t));
        p += sizeof(cookie_t);
        memcpy(p, &iph1->index.r_ck, sizeof(cookie_t));
        p += sizeof(cookie_t);

        memcpy(p, iph1->sa->v, iph1->sa->l);
        p += iph1->sa->l;

        bp = (sw == GENERATE ? iph1->id : iph1->id_p);
        memcpy(p, bp->v, bp->l);
        p += bp->l;

        plog(LLV_DEBUG, LOCATION, NULL, "HASH_I with:\n");
        plogdump(LLV_DEBUG, buf->v, buf->l);

        /* compute HASH */
        res = oakley_prf(hashkey, buf, iph1);
        if (res == NULL)
                goto end;

        plog(LLV_DEBUG, LOCATION, NULL, "HASH_I computed:\n");
        plogdump(LLV_DEBUG, res->v, res->l);

end:
        if (hash != NULL)
                vfree(hash);
        if (buf != NULL)
                vfree(buf);
        return res;
}

/*
 * compute HASH_R on base mode for signature method.
 * base:
 * HASH_R = prf(hash(Ni_b | Nr_b), g^xi | g^xr | CKY-I | CKY-R | SAi_b | IDii_b)
 */
vchar_t *
oakley_ph1hash_base_r(struct ph1handle *iph1, int sw)
{
        vchar_t *buf = NULL, *res = NULL, *bp;
        vchar_t *hash = NULL;
        char *p;
        size_t len;

        /* sanity check */
        if (iph1->etype != ISAKMP_ETYPE_BASE) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid etype for this hash function\n");
                return NULL;
        }

        switch (iph1->approval->authmethod) {
        case OAKLEY_ATTR_AUTH_METHOD_DSSSIG:
        case OAKLEY_ATTR_AUTH_METHOD_RSASIG:
#ifdef ENABLE_HYBRID
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I:
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R:
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I:
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I:
#endif
                break;
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "not supported authentication method %d\n",
                        iph1->approval->authmethod);
                return NULL;
        }

        /* make hash for seed */
        len = iph1->nonce->l + iph1->nonce_p->l;
        buf = vmalloc(len);
        if (buf == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get hash buffer\n");
                goto end;
        }
        p = buf->v;

        bp = (sw == GENERATE ? iph1->nonce_p : iph1->nonce);
        memcpy(p, bp->v, bp->l);
        p += bp->l;

        bp = (sw == GENERATE ? iph1->nonce : iph1->nonce_p);
        memcpy(p, bp->v, bp->l);
        p += bp->l;

        hash = oakley_hash(buf, iph1);
        if (hash == NULL)
                goto end;
        vfree(buf);
        buf = NULL;

        /* make really hash */
        len = (sw == GENERATE ? iph1->dhpub_p->l : iph1->dhpub->l)
                + (sw == GENERATE ? iph1->dhpub->l : iph1->dhpub_p->l)
                + sizeof(cookie_t) * 2
                + iph1->sa->l
                + (sw == GENERATE ? iph1->id_p->l : iph1->id->l);
        buf = vmalloc(len);
        if (buf == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get hash buffer\n");
                goto end;
        }
        p = buf->v;


        bp = (sw == GENERATE ? iph1->dhpub_p : iph1->dhpub);
        memcpy(p, bp->v, bp->l);
        p += bp->l;

        bp = (sw == GENERATE ? iph1->dhpub : iph1->dhpub_p);
        memcpy(p, bp->v, bp->l);
        p += bp->l;

        memcpy(p, &iph1->index.i_ck, sizeof(cookie_t));
        p += sizeof(cookie_t);
        memcpy(p, &iph1->index.r_ck, sizeof(cookie_t));
        p += sizeof(cookie_t);

        memcpy(p, iph1->sa->v, iph1->sa->l);
        p += iph1->sa->l;

        bp = (sw == GENERATE ? iph1->id_p : iph1->id);
        memcpy(p, bp->v, bp->l);
        p += bp->l;

        plog(LLV_DEBUG, LOCATION, NULL, "HASH_R with:\n");
        plogdump(LLV_DEBUG, buf->v, buf->l);

        /* compute HASH */
        res = oakley_prf(hash, buf, iph1);
        if (res == NULL)
                goto end;

        plog(LLV_DEBUG, LOCATION, NULL, "HASH_R computed:\n");
        plogdump(LLV_DEBUG, res->v, res->l);

end:
        if (buf != NULL)
                vfree(buf);
        if (hash)
                vfree(hash);
        return res;
}

/*
 * compute each authentication method in phase 1.
 * OUT:
 *      0:      OK
 *      -1:     error
 *      other:  error to be reply with notification.
 *              the value is notification type.
 */
int
oakley_validate_auth(struct ph1handle *iph1)
{
        vchar_t *my_hash = NULL;
        int result;
        int no_verify_needed = -1;
#ifdef HAVE_GSSAPI
        vchar_t *gsshash = NULL;
#endif
#ifdef ENABLE_STATS
        struct timeval start, end;
#endif

#ifdef ENABLE_STATS
        gettimeofday(&start, NULL);
#endif

        switch (iph1->approval->authmethod) {
        case OAKLEY_ATTR_AUTH_METHOD_PSKEY:
#ifdef ENABLE_HYBRID
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R:
#endif
                /* validate HASH */
            {
                char *r_hash;

                if (iph1->id_p == NULL || iph1->pl_hash == NULL) {
                        plog(LLV_ERROR, LOCATION, iph1->remote,
                                "few isakmp message received.\n");
                        return ISAKMP_NTYPE_PAYLOAD_MALFORMED;
                }
#ifdef ENABLE_HYBRID
                if (iph1->approval->authmethod == OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I &&
                    ((iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_XAUTH) == 0))
                {
                        plog(LLV_ERROR, LOCATION, NULL, "No SIG was passed, "
                            "hybrid auth is enabled, "
                            "but peer is no Xauth compliant\n");
                        return ISAKMP_NTYPE_SITUATION_NOT_SUPPORTED;
                }
#endif
                r_hash = (caddr_t)(iph1->pl_hash + 1);

                plog(LLV_DEBUG, LOCATION, NULL, "HASH received:\n");
                plogdump(LLV_DEBUG, r_hash,
                        ntohs(iph1->pl_hash->h.len) - sizeof(*iph1->pl_hash));

                switch (iph1->etype) {
                case ISAKMP_ETYPE_IDENT:
                case ISAKMP_ETYPE_AGG:
                        my_hash = oakley_ph1hash_common(iph1, VALIDATE);
                        break;
                case ISAKMP_ETYPE_BASE:
                        if (iph1->side == INITIATOR)
                                my_hash = oakley_ph1hash_common(iph1, VALIDATE);
                        else
                                my_hash = oakley_ph1hash_base_i(iph1, VALIDATE);
                        break;
                default:
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid etype %d\n", iph1->etype);
                        return ISAKMP_NTYPE_INVALID_EXCHANGE_TYPE;
                }
                if (my_hash == NULL)
                        return ISAKMP_INTERNAL_ERROR;

                result = memcmp(my_hash->v, r_hash, my_hash->l);
                vfree(my_hash);

                if (result) {
                        plog(LLV_ERROR, LOCATION, NULL, "HASH mismatched\n");
                        return ISAKMP_NTYPE_INVALID_HASH_INFORMATION;
                }

                plog(LLV_DEBUG, LOCATION, NULL, "HASH for PSK validated.\n");
            }
                break;
#ifdef ENABLE_HYBRID
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I:
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R:
                no_verify_needed = 0;
                __attribute__((__fallthrough__));
                /*FALLTHROUGH*/
#endif
        case OAKLEY_ATTR_AUTH_METHOD_DSSSIG:
        case OAKLEY_ATTR_AUTH_METHOD_RSASIG:
            {
                int error = 0;
                int certtype;

                /* validation */
                if (iph1->id_p == NULL) {
                        plog(LLV_ERROR, LOCATION, iph1->remote,
                                "no ID payload was passed.\n");
                        return ISAKMP_NTYPE_PAYLOAD_MALFORMED;
                }
                if (iph1->sig_p == NULL) {
                        plog(LLV_ERROR, LOCATION, iph1->remote,
                                "no SIG payload was passed.\n");
                        return ISAKMP_NTYPE_PAYLOAD_MALFORMED;
                }

                plog(LLV_DEBUG, LOCATION, NULL, "SIGN passed:\n");
                plogdump(LLV_DEBUG, iph1->sig_p->v, iph1->sig_p->l);

                /* get peer's cert */
                certtype = oakley_get_certtype(iph1->rmconf->peerscert);
                switch (certtype) {
                case ISAKMP_CERT_NONE:
                        /* expect to receive one from peer */
                        if (iph1->cert_p == NULL) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                     "no peer's CERT payload found.\n");
                                return ISAKMP_INTERNAL_ERROR;
                        }
                        /* verify the cert if needed */
                        if (!iph1->rmconf->verify_cert)
                                break;

                        switch (oakley_get_certtype(iph1->cert_p)) {
                        case ISAKMP_CERT_X509SIGN: {
                                char path[MAXPATHLEN];
                                char *ca;

                                if (iph1->rmconf->cacertfile != NULL) {
                                        getpathname(path, sizeof(path),
                                                    LC_PATHTYPE_CERT,
                                                    iph1->rmconf->cacertfile);
                                        ca = path;
                                } else {
                                        ca = NULL;
                                }

                                error = eay_check_x509cert(
                                        iph1->cert_p,
                                        lcconf->pathinfo[LC_PATHTYPE_CERT],
                                        ca, 0);
                                break;
                                }
                        default:
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "peers_cert certtype %d was not expected\n",
                                        certtype);
                                return ISAKMP_INTERNAL_ERROR;
                        }

                        if (error != 0) {
                                plog(LLV_ERROR, LOCATION, iph1->remote,
                                     "the peer's certificate is not verified.\n");
                                return ISAKMP_NTYPE_INVALID_CERT_AUTHORITY;
                        }
                        break;
                case ISAKMP_CERT_X509SIGN:
                        if (iph1->rmconf->peerscert == NULL) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                     "no peer's CERT file found.\n");
                                return ISAKMP_INTERNAL_ERROR;
                        }
                        /* don't use received cert */
                        if (iph1->cert_p != NULL) {
                                vfree(iph1->cert_p);
                                iph1->cert_p = NULL;
                        }
                        /* copy from remoteconf instead */
                        iph1->cert_p = vdup(iph1->rmconf->peerscert);
                        break;
                case ISAKMP_CERT_PLAINRSA:
                        if (get_plainrsa_fromlocal(iph1, 0))
                                return ISAKMP_INTERNAL_ERROR;
                        /* suppress CERT validation warning, unless hybrid mode in use */
                        if (no_verify_needed == -1)
                                no_verify_needed = 1;
                        break;
                case ISAKMP_CERT_DNS:
                        /* don't use received cert */
                        if (iph1->cert_p != NULL) {
                                vfree(iph1->cert_p);
                                iph1->cert_p = NULL;
                        }

                        iph1->cert_p = dnssec_getcert(iph1->id_p);
                        if (iph1->cert_p == NULL) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                     "no CERT RR found.\n");
                                return ISAKMP_INTERNAL_ERROR;
                        }
                        break;
                default:
                        plog(LLV_ERROR, LOCATION, NULL,
                             "invalid certificate type: %d\n",
                             oakley_get_certtype(iph1->rmconf->peerscert));
                        return ISAKMP_INTERNAL_ERROR;
                }

                /* compare ID payload and certificate name */
                if ((error = oakley_check_certid(iph1)) != 0)
                        return error;

                /* Generate a warning unless verify_cert */
                if (iph1->rmconf->verify_cert) {
                        plog(LLV_DEBUG, LOCATION, iph1->remote,
                             "CERT validated\n");
                } else if (no_verify_needed != 1) {
                        plog(LLV_WARNING, LOCATION, iph1->remote,
                             "CERT validation disabled by configuration\n");
                }

                /* compute hash */
                switch (iph1->etype) {
                case ISAKMP_ETYPE_IDENT:
                case ISAKMP_ETYPE_AGG:
                        my_hash = oakley_ph1hash_common(iph1, VALIDATE);
                        break;
                case ISAKMP_ETYPE_BASE:
                        if (iph1->side == INITIATOR)
                                my_hash = oakley_ph1hash_base_r(iph1, VALIDATE);
                        else
                                my_hash = oakley_ph1hash_base_i(iph1, VALIDATE);
                        break;
                default:
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid etype %d\n", iph1->etype);
                        return ISAKMP_NTYPE_INVALID_EXCHANGE_TYPE;
                }
                if (my_hash == NULL)
                        return ISAKMP_INTERNAL_ERROR;

                /* check signature */
                certtype = oakley_get_certtype(iph1->cert_p);
                if (certtype == ISAKMP_CERT_NONE)
                        certtype = oakley_get_certtype(iph1->rmconf->peerscert);
                switch (certtype) {
                case ISAKMP_CERT_X509SIGN:
                case ISAKMP_CERT_DNS:
                        error = eay_check_x509sign(my_hash,
                                                   iph1->sig_p,
                                                   iph1->cert_p);
                        break;
                case ISAKMP_CERT_PLAINRSA:
                        iph1->rsa_p = rsa_try_check_rsasign(my_hash,
                                        iph1->sig_p, iph1->rsa_candidates);
                        error = iph1->rsa_p ? 0 : -1;
                        genlist_free(iph1->rsa_candidates, NULL);
                        iph1->rsa_candidates = NULL;
                        break;
                default:
                        plog(LLV_ERROR, LOCATION, NULL,
                             "cannot check signature for certtype %d\n",
                             certtype);
                        vfree(my_hash);
                        return ISAKMP_INTERNAL_ERROR;
                }

                vfree(my_hash);
                if (error != 0) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "Invalid SIG.\n");
                        return ISAKMP_NTYPE_INVALID_SIGNATURE;
                }
                plog(LLV_DEBUG, LOCATION, NULL, "SIG authenticated\n");
            }
                break;
#ifdef ENABLE_HYBRID
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R:
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R:
            {
                if ((iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_XAUTH) == 0) {
                        plog(LLV_ERROR, LOCATION, NULL, "No SIG was passed, "
                            "hybrid auth is enabled, "
                            "but peer is no Xauth compliant\n");
                        return ISAKMP_NTYPE_SITUATION_NOT_SUPPORTED;
                }
                plog(LLV_INFO, LOCATION, NULL, "No SIG was passed, "
                    "but hybrid auth is enabled\n");

                return 0;
            }
#endif
#ifdef HAVE_GSSAPI
        case OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB:
                /* check if we're not into XAUTH_PSKEY_I instead */
#ifdef ENABLE_HYBRID
                if (iph1->rmconf->xauth)
                        break;
#endif
                switch (iph1->etype) {
                case ISAKMP_ETYPE_IDENT:
                case ISAKMP_ETYPE_AGG:
                        my_hash = oakley_ph1hash_common(iph1, VALIDATE);
                        break;
                default:
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid etype %d\n", iph1->etype);
                        return ISAKMP_NTYPE_INVALID_EXCHANGE_TYPE;
                }

                if (my_hash == NULL) {
                        if (gssapi_more_tokens(iph1))
                                return ISAKMP_NTYPE_INVALID_EXCHANGE_TYPE;
                        else
                                return ISAKMP_NTYPE_INVALID_HASH_INFORMATION;
                }

                gsshash = gssapi_unwraphash(iph1);
                if (gsshash == NULL) {
                        vfree(my_hash);
                        return ISAKMP_NTYPE_INVALID_HASH_INFORMATION;
                }

                result = memcmp(my_hash->v, gsshash->v, my_hash->l);
                vfree(my_hash);
                vfree(gsshash);

                if (result) {
                        plog(LLV_ERROR, LOCATION, NULL, "HASH mismatched\n");
                        return ISAKMP_NTYPE_INVALID_HASH_INFORMATION;
                }
                plog(LLV_DEBUG, LOCATION, NULL, "hash compared OK\n");
                break;
#endif
        case OAKLEY_ATTR_AUTH_METHOD_RSAENC:
        case OAKLEY_ATTR_AUTH_METHOD_RSAREV:
#ifdef ENABLE_HYBRID
        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
                if (iph1->id_p == NULL || iph1->pl_hash == NULL) {
                        plog(LLV_ERROR, LOCATION, iph1->remote,
                                "few isakmp message received.\n");
                        return ISAKMP_NTYPE_PAYLOAD_MALFORMED;
                }
                plog(LLV_ERROR, LOCATION, iph1->remote,
                        "not supported authmethod type %s\n",
                        s_oakley_attr_method(iph1->approval->authmethod));
                return ISAKMP_INTERNAL_ERROR;
        default:
                plog(LLV_ERROR, LOCATION, iph1->remote,
                        "invalid authmethod %d why ?\n",
                        iph1->approval->authmethod);
                return ISAKMP_INTERNAL_ERROR;
        }
#ifdef ENABLE_STATS
        gettimeofday(&end, NULL);
        syslog(LOG_NOTICE, "%s(%s): %8.6f", __func__,
                s_oakley_attr_method(iph1->approval->authmethod),
                timedelta(&start, &end));
#endif

        return 0;
}

/* get my certificate
 * NOTE: include certificate type.
 */
int
oakley_getmycert(struct ph1handle *iph1)
{
        switch (oakley_get_certtype(iph1->rmconf->mycert)) {
        case ISAKMP_CERT_X509SIGN:
                if (iph1->cert)
                        return 0;
                iph1->cert = vdup(iph1->rmconf->mycert);
                break;
        case ISAKMP_CERT_PLAINRSA:
                if (iph1->rsa)
                        return 0;
                return get_plainrsa_fromlocal(iph1, 1);
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                     "Unknown certtype #%d\n",
                     oakley_get_certtype(iph1->rmconf->mycert));
                return -1;
        }

        return 0;
}

static int
get_plainrsa_fromlocal(struct ph1handle *iph1, int my)
{
        int error = -1;

        iph1->rsa_candidates = rsa_lookup_keys(iph1, my);
        if (!iph1->rsa_candidates || 
            rsa_list_count(iph1->rsa_candidates) == 0) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "%s RSA key not found for %s\n",
                        my ? "Private" : "Public",
                        saddr2str_fromto("%s <-> %s", 
                        iph1->local, iph1->remote));
                goto end;
        }

        if (my && rsa_list_count(iph1->rsa_candidates) > 1) {
                plog(LLV_WARNING, LOCATION, NULL,
                        "More than one (=%lu) private "
                        "PlainRSA key found for %s\n",
                        rsa_list_count(iph1->rsa_candidates),
                        saddr2str_fromto("%s <-> %s", 
                        iph1->local, iph1->remote));
                plog(LLV_WARNING, LOCATION, NULL,
                        "This may have unpredictable results, "
                        "i.e. wrong key could be used!\n");
                plog(LLV_WARNING, LOCATION, NULL,
                        "Consider using only one single private "
                        "key for all peers...\n");
        }
        if (my) {
                iph1->rsa = ((struct rsa_key *)
                    genlist_next(iph1->rsa_candidates, NULL))->rsa;

                genlist_free(iph1->rsa_candidates, NULL);
                iph1->rsa_candidates = NULL;

                if (iph1->rsa == NULL)
                        goto end;
        }

        error = 0;

end:
        return error;
}

/* get signature */
int
oakley_getsign(struct ph1handle *iph1)
{
        char path[MAXPATHLEN];
        vchar_t *privkey = NULL;
        int error = -1;

        switch (oakley_get_certtype(iph1->rmconf->mycert)) {
        case ISAKMP_CERT_X509SIGN:
        case ISAKMP_CERT_DNS:
                if (iph1->rmconf->myprivfile == NULL) {
                        plog(LLV_ERROR, LOCATION, NULL, "no cert defined.\n");
                        goto end;
                }

                /* make private file name */
                getpathname(path, sizeof(path),
                        LC_PATHTYPE_CERT,
                        iph1->rmconf->myprivfile);
                privkey = privsep_eay_get_pkcs1privkey(path);
                if (privkey == NULL) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "failed to get private key.\n");
                        goto end;
                }
                plog(LLV_DEBUG2, LOCATION, NULL, "private key:\n");
                plogdump(LLV_DEBUG2, privkey->v, privkey->l);
                iph1->sig = eay_get_x509sign(iph1->hash, privkey);
                break;
        case ISAKMP_CERT_PLAINRSA:
                iph1->sig = eay_get_rsasign(iph1->hash, iph1->rsa);
                break;
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                     "Unknown certtype #%d\n",
                     oakley_get_certtype(iph1->rmconf->mycert));
                goto end;
        }

        if (iph1->sig == NULL) {
                plog(LLV_ERROR, LOCATION, NULL, "failed to sign.\n");
                goto end;
        }

        plog(LLV_DEBUG, LOCATION, NULL, "SIGN computed:\n");
        plogdump(LLV_DEBUG, iph1->sig->v, iph1->sig->l);

        error = 0;

end:
        if (privkey != NULL)
                vfree(privkey);

        return error;
}

/*
 * compare certificate name and ID value.
 */
static int
oakley_check_certid(struct ph1handle *iph1)
{
        struct ipsecdoi_id_b *id_b;
        vchar_t *name = NULL;
        char *altname = NULL;
        int idlen, type;
        int error;

        if (iph1->rmconf == NULL || iph1->rmconf->verify_cert == FALSE)
                return 0;

        if (iph1->id_p == NULL || iph1->cert_p == NULL) {
                plog(LLV_ERROR, LOCATION, iph1->remote, "no ID nor CERT found.\n");
                return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
        }

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

        switch (id_b->type) {
        case IPSECDOI_ID_DER_ASN1_DN:
                name = eay_get_x509asn1subjectname(iph1->cert_p);
                if (!name) {
                        plog(LLV_ERROR, LOCATION, iph1->remote,
                                "failed to get subjectName\n");
                        return ISAKMP_NTYPE_INVALID_CERTIFICATE;
                }
                if (idlen != name->l) {
                        plog(LLV_ERROR, LOCATION, iph1->remote,
                                "Invalid ID length in phase 1.\n");
                        vfree(name);
                        return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
                }
                error = memcmp(id_b + 1, name->v, idlen);
                if (error != 0) {
                        plog(LLV_ERROR, LOCATION, iph1->remote,
                                "ID mismatched with ASN1 SubjectName.\n");
                        plogdump(LLV_DEBUG, id_b + 1, idlen);
                        plogdump(LLV_DEBUG, name->v, idlen);
                        if (iph1->rmconf->verify_identifier) {
                                vfree(name);
                                return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
                        }
                }
                vfree(name);
                return 0;
        case IPSECDOI_ID_IPV4_ADDR:
        case IPSECDOI_ID_IPV6_ADDR:
        {
                /*
                 * converting to binary from string because openssl return
                 * a string even if object is a binary.
                 * XXX fix it !  access by ASN.1 directly without.
                 */
                struct addrinfo hints, *res;
                caddr_t a = NULL;
                int pos;

                for (pos = 1; ; pos++) {
                        if (eay_get_x509subjectaltname(iph1->cert_p,
                                        &altname, &type, pos) !=0) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "failed to get subjectAltName\n");
                                return ISAKMP_NTYPE_INVALID_CERTIFICATE;
                        }

                        /* it's the end condition of the loop. */
                        if (!altname) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "no proper subjectAltName.\n");
                                return ISAKMP_NTYPE_INVALID_CERTIFICATE;
                        }

                        if (check_typeofcertname(id_b->type, type) == 0)
                                break;

                        /* next name */
                        racoon_free(altname);
                        altname = NULL;
                }
                memset(&hints, 0, sizeof(hints));
                hints.ai_family = PF_UNSPEC;
                hints.ai_socktype = SOCK_RAW;
                hints.ai_flags = AI_NUMERICHOST;
                error = getaddrinfo(altname, NULL, &hints, &res);
                racoon_free(altname);
                altname = NULL;
                if (error != 0) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "no proper subjectAltName.\n");
                        return ISAKMP_NTYPE_INVALID_CERTIFICATE;
                }
                switch (res->ai_family) {
                case AF_INET:
                        a = (caddr_t)&((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr;
                        break;
#ifdef INET6
                case AF_INET6:
                        a = (caddr_t)&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr.s6_addr;
                        break;
#endif
                default:
                        plog(LLV_ERROR, LOCATION, NULL,
                                "family not supported: %d.\n", res->ai_family);
                        freeaddrinfo(res);
                        return ISAKMP_NTYPE_INVALID_CERTIFICATE;
                }
                error = memcmp(id_b + 1, a, idlen);
                freeaddrinfo(res);
                vfree(name);
                if (error != 0) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "ID mismatched with subjectAltName.\n");
                        plogdump(LLV_DEBUG, id_b + 1, idlen);
                        plogdump(LLV_DEBUG, a, idlen);
                        if (iph1->rmconf->verify_identifier)
                                return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
                }
                return 0;
        }
        case IPSECDOI_ID_FQDN:
        case IPSECDOI_ID_USER_FQDN:
        {
                int pos;

                for (pos = 1; ; pos++) {
                        if (eay_get_x509subjectaltname(iph1->cert_p,
                                        &altname, &type, pos) != 0){
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "failed to get subjectAltName\n");
                                return ISAKMP_NTYPE_INVALID_CERTIFICATE;
                        }

                        /* it's the end condition of the loop. */
                        if (!altname) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "no proper subjectAltName.\n");
                                return ISAKMP_NTYPE_INVALID_CERTIFICATE;
                        }

                        if (check_typeofcertname(id_b->type, type) == 0)
                                break;

                        /* next name */
                        racoon_free(altname);
                        altname = NULL;
                }
                if (idlen != strlen(altname)) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "Invalid ID length in phase 1.\n");
                        racoon_free(altname);
                        return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
                }
                if (check_typeofcertname(id_b->type, type) != 0) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "ID type mismatched. ID: %s CERT: %s.\n",
                                s_ipsecdoi_ident(id_b->type),
                                s_ipsecdoi_ident(type));
                        racoon_free(altname);
                        return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
                }
                error = memcmp(id_b + 1, altname, idlen);
                if (error) {
                        plog(LLV_ERROR, LOCATION, NULL, "ID mismatched.\n");
                        plogdump(LLV_DEBUG, id_b + 1, idlen);
                        plogdump(LLV_DEBUG, altname, idlen);
                        racoon_free(altname);
                        return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
                }
                racoon_free(altname);
                return 0;
        }
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "Inpropper ID type passed: %s.\n",
                        s_ipsecdoi_ident(id_b->type));
                return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
        }
        /*NOTREACHED*/
}

static int
check_typeofcertname(int doi, int genid)
{
        switch (doi) {
        case IPSECDOI_ID_IPV4_ADDR:
        case IPSECDOI_ID_IPV4_ADDR_SUBNET:
        case IPSECDOI_ID_IPV6_ADDR:
        case IPSECDOI_ID_IPV6_ADDR_SUBNET:
        case IPSECDOI_ID_IPV4_ADDR_RANGE:
        case IPSECDOI_ID_IPV6_ADDR_RANGE:
                if (genid != GENT_IPADD)
                        return -1;
                return 0;
        case IPSECDOI_ID_FQDN:
                if (genid != GENT_DNS)
                        return -1;
                return 0;
        case IPSECDOI_ID_USER_FQDN:
                if (genid != GENT_EMAIL)
                        return -1;
                return 0;
        case IPSECDOI_ID_DER_ASN1_DN: /* should not be passed to this function*/
        case IPSECDOI_ID_DER_ASN1_GN:
        case IPSECDOI_ID_KEY_ID:
        default:
                return -1;
        }
        /*NOTREACHED*/
}

/*
 * save certificate including certificate type.
 */
int
oakley_savecert(struct ph1handle *iph1, struct isakmp_gen *gen)
{
        vchar_t **c;
        uint8_t type;
        STACK_OF(X509) *certs=NULL;
        PKCS7 *p7;

        type = *(uint8_t *)(gen + 1) & 0xff;

        switch (type) {
        case ISAKMP_CERT_DNS:
                plog(LLV_WARNING, LOCATION, NULL,
                        "CERT payload is unnecessary in DNSSEC. "
                        "ignore this CERT payload.\n");
                return 0;
        case ISAKMP_CERT_PKCS7:
        case ISAKMP_CERT_PGP:
        case ISAKMP_CERT_X509SIGN:
        case ISAKMP_CERT_KERBEROS:
        case ISAKMP_CERT_SPKI:
                c = &iph1->cert_p;
                break;
        case ISAKMP_CERT_CRL:
                c = &iph1->crl_p;
                break;
        case ISAKMP_CERT_X509KE:
        case ISAKMP_CERT_X509ATTR:
        case ISAKMP_CERT_ARL:
                plog(LLV_ERROR, LOCATION, NULL,
                        "No supported such CERT type %d\n", type);
                return -1;
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "Invalid CERT type %d\n", type);
                return -1;
        }

        /* XXX choice the 1th cert, ignore after the cert. */ 
        /* XXX should be processed. */
        if (*c) {
                plog(LLV_WARNING, LOCATION, NULL,
                        "ignore 2nd CERT payload.\n");
                return 0;
        }

        if (type == ISAKMP_CERT_PKCS7) {
                u_char *bp;
                int i;

                /* Skip the header */
                bp = (u_char *)(gen + 1);
                /* And the first byte is the certificate type, 
                 * we know that already
                 */
                bp++;
                p7 = d2i_PKCS7(NULL, (void *)&bp, 
                    ntohs(gen->len) - sizeof(*gen) - 1);

                if (!p7) {
                        plog(LLV_ERROR, LOCATION, NULL,
                             "Failed to parse PKCS#7 CERT.\n");
                        return -1;
                }

                /* Copied this from the openssl pkcs7 application;
                 * there"s little by way of documentation for any of
                 * it. I can only presume it"s correct.
                 */
                
                i = OBJ_obj2nid(p7->type);
                switch (i) {
                case NID_pkcs7_signed:
                        certs=p7->d.sign->cert;
                        break;
                case NID_pkcs7_signedAndEnveloped:
                        certs=p7->d.signed_and_enveloped->cert;
                        break;
                default:
                         break;
                }

                if (!certs) {
                        plog(LLV_ERROR, LOCATION, NULL,
                             "CERT PKCS#7 bundle contains no certs.\n");
                        PKCS7_free(p7);
                        return -1;
                }

                for (i = 0; i < sk_X509_num(certs); i++) {
                        X509 *cert = sk_X509_value(certs,i);

                        plog(LLV_DEBUG, LOCATION, NULL, 
                             "Trying PKCS#7 cert %d.\n", i);

                        /* We'll just try each cert in turn */
                        *c = dump_x509(cert);

                        if (!*c) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                     "Failed to get CERT buffer.\n");
                                continue;
                        }

                        /* Ignore cert if it doesn't match identity
                         * XXX If verify cert is disabled, we still just take
                         * the first certificate....
                         */
                        if (oakley_check_certid(iph1)) {
                                plog(LLV_DEBUG, LOCATION, NULL,
                                     "Discarding CERT: does not match ID.\n");
                                vfree((*c));
                                *c = NULL;
                                continue;
                        }

                        {
                                char *p = eay_get_x509text(*c);
                                plog(LLV_DEBUG, LOCATION, NULL, "CERT saved:\n");
                                plogdump(LLV_DEBUG, (*c)->v, (*c)->l);
                                plog(LLV_DEBUG, LOCATION, NULL, "%s", 
                                     p ? p : "\n");
                                racoon_free(p);
                        }
                        break;
                }
                PKCS7_free(p7);
        } else {
                *c = dump_isakmp_payload(gen);
                if (!*c) {
                        plog(LLV_ERROR, LOCATION, NULL,
                             "Failed to get CERT buffer.\n");
                        return -1;
                }

                switch (type) {
                case ISAKMP_CERT_PGP:
                case ISAKMP_CERT_X509SIGN:
                case ISAKMP_CERT_KERBEROS:
                case ISAKMP_CERT_SPKI:
                        /* Ignore cert if it doesn't match identity
                         * XXX If verify cert is disabled, we still just take
                         * the first certificate....
                         */
                        if (oakley_check_certid(iph1)){
                                plog(LLV_DEBUG, LOCATION, NULL,
                                     "Discarding CERT: does not match ID.\n");
                                vfree((*c));
                                *c = NULL;
                                return 0;
                        }

                        {
                                char *p = eay_get_x509text(*c);
                                plog(LLV_DEBUG, LOCATION, NULL, "CERT saved:\n");
                                plogdump(LLV_DEBUG, (*c)->v, (*c)->l);
                                plog(LLV_DEBUG, LOCATION, NULL, "%s", p ? p : "\n");
                                racoon_free(p);
                        }
                        break;
                case ISAKMP_CERT_CRL:
                        plog(LLV_DEBUG, LOCATION, NULL, "CRL saved:\n");
                        plogdump(LLV_DEBUG, (*c)->v, (*c)->l);
                        break;
                case ISAKMP_CERT_X509KE:
                case ISAKMP_CERT_X509ATTR:
                case ISAKMP_CERT_ARL:
                default:
                        /* XXX */
                        vfree(*c);
                        *c = NULL;
                        return 0;
                }
        }
        
        return 0;
}

/*
 * save certificate including certificate type.
 */
int
oakley_savecr(struct ph1handle *iph1, struct isakmp_gen *gen)
{
        vchar_t *cert;
        vchar_t **c;
        uint8_t type;

        type = *(uint8_t *)(gen + 1) & 0xff;
        switch (type) {
        case ISAKMP_CERT_DNS:
                plog(LLV_WARNING, LOCATION, NULL,
                        "CERT payload is unnecessary in DNSSEC\n");
                /*FALLTHRU*/
        case ISAKMP_CERT_PKCS7:
        case ISAKMP_CERT_PGP:
        case ISAKMP_CERT_X509SIGN:
        case ISAKMP_CERT_KERBEROS:
        case ISAKMP_CERT_SPKI:
                c = &iph1->cr_p;
                break;
        case ISAKMP_CERT_X509KE:
        case ISAKMP_CERT_X509ATTR:
        case ISAKMP_CERT_ARL:
                plog(LLV_ERROR, LOCATION, NULL,
                        "No supported such CR type %d\n", type);
                return -1;
        case ISAKMP_CERT_CRL:
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "Invalid CR type %d\n", type);
                return -1;
        }

        /* Already found an acceptable CR? */
        if (*c != NULL)
                return 0;

        cert = dump_isakmp_payload(gen);
        if (cert == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "Failed to get CR buffer.\n");
                return -1;
        }

        plog(LLV_DEBUG, LOCATION, NULL, "CR received:\n");
        plogdump(LLV_DEBUG, cert->v, cert->l);

        *c = cert;
        if (resolveph1rmconf(iph1) == 0) {
                /* Found unique match */
                plog(LLV_DEBUG, LOCATION, NULL, "CR saved.\n");
        } else {
                /* Still ambiguous or matches nothing, ignore this CR */
                *c = NULL;
                vfree(cert);
        }
        return 0;
}

/*
 * Add a single CR.
 */
struct append_cr_ctx {
        struct ph1handle *iph1;
        struct payload_list *plist;
};

static int
oakley_append_rmconf_cr(struct remoteconf *rmconf, void *ctx)
{
        struct append_cr_ctx *actx = (struct append_cr_ctx *) ctx;
        vchar_t *buf, *asn1dn = NULL;
        int type;

        /* Do we want to send CR about this? */
        if (rmconf->send_cr == FALSE)
                return 0;

        if (rmconf->peerscert != NULL) {
                type = oakley_get_certtype(rmconf->peerscert);
                asn1dn = eay_get_x509asn1issuername(rmconf->peerscert);
        } else if (rmconf->cacert != NULL) {
                type = oakley_get_certtype(rmconf->cacert);
                asn1dn = eay_get_x509asn1subjectname(rmconf->cacert);
        } else
                return 0;

        if (asn1dn == NULL) {
                plog(LLV_ERROR, LOCATION, actx->iph1->remote,
                     "Failed to get CR ASN1 DN from certificate\n");
                return 0;
        }

        buf = vmalloc(1 + asn1dn->l);
        if (buf == NULL)
                goto err;

        buf->v[0] = type;
        memcpy(&buf->v[1], asn1dn->v, asn1dn->l);

        plog(LLV_DEBUG, LOCATION, actx->iph1->remote,
             "appending CR: %s\n",
             s_isakmp_certtype(buf->v[0]));
        plogdump(LLV_DEBUG, buf->v, buf->l);

        actx->plist = isakmp_plist_append_full(actx->plist, buf, ISAKMP_NPTYPE_CR, 1);

err:
        vfree(asn1dn);
        return 0;
}

/*
 * Append list of acceptable CRs.
 * RFC2048 3.10
 */
struct payload_list *
oakley_append_cr(struct payload_list *plist, struct ph1handle *iph1)
{
        struct append_cr_ctx ctx;
        struct rmconfselector sel;

        ctx.iph1 = iph1;
        ctx.plist = plist;
        if (iph1->rmconf == NULL) {
                rmconf_selector_from_ph1(&sel, iph1);
                enumrmconf(&sel, oakley_append_rmconf_cr, &ctx);
        } else {
                oakley_append_rmconf_cr(iph1->rmconf, &ctx);
        }

        return ctx.plist;
}

/*
 * check peer's CR.
 */
int
oakley_checkcr(struct ph1handle *iph1)
{
        int type;

        if (iph1->cr_p == NULL)
                return 0;

        plog(LLV_DEBUG, LOCATION, iph1->remote,
                "peer transmitted CR: %s\n",
                s_isakmp_certtype(oakley_get_certtype(iph1->cr_p)));

        type = oakley_get_certtype(iph1->cr_p);
        if (type != oakley_get_certtype(iph1->rmconf->mycert)) {
                plog(LLV_ERROR, LOCATION, iph1->remote,
                     "such a cert type isn't supported: %d\n",
                     type);
                return -1;
        }

        return 0;
}

/*
 * check to need CR payload.
 */
int
oakley_needcr(int type)
{
        switch (type) {
        case OAKLEY_ATTR_AUTH_METHOD_DSSSIG:
        case OAKLEY_ATTR_AUTH_METHOD_RSASIG:
#ifdef ENABLE_HYBRID
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I:
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R:
#endif
                return 1;
        default:
                return 0;
        }
        /*NOTREACHED*/
}

/*
 * compute SKEYID
 * see seciton 5. Exchanges in RFC 2409
 * psk: SKEYID = prf(pre-shared-key, Ni_b | Nr_b)
 * sig: SKEYID = prf(Ni_b | Nr_b, g^ir)
 * enc: SKEYID = prf(H(Ni_b | Nr_b), CKY-I | CKY-R)
 */
int
oakley_skeyid(struct ph1handle *iph1)
{
        vchar_t *buf = NULL, *bp;
        char *p;
        size_t len;
        int error = -1;
        
        /* SKEYID */
        switch (iph1->approval->authmethod) {
        case OAKLEY_ATTR_AUTH_METHOD_PSKEY:
#ifdef ENABLE_HYBRID
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R:
#endif
                if (iph1->etype != ISAKMP_ETYPE_IDENT) {
                        iph1->authstr = getpskbyname(iph1->id_p);
                        if (iph1->authstr == NULL) {
                                if (iph1->rmconf->verify_identifier) {
                                        plog(LLV_ERROR, LOCATION, iph1->remote,
                                                "couldn't find the pskey.\n");
                                        goto end;
                                }
                                plog(LLV_NOTIFY, LOCATION, iph1->remote,
                                        "couldn't find the proper pskey, "
                                        "try to get one by the peer's address.\n");
                        }
                }
                if (iph1->authstr == NULL) {
                        /*
                         * If the exchange type is the main mode or if it's
                         * failed to get the psk by ID, racoon try to get
                         * the psk by remote IP address.
                         * It may be nonsense.
                         */
                        iph1->authstr = getpskbyaddr(iph1->remote);
                        if (iph1->authstr == NULL) {
                                plog(LLV_ERROR, LOCATION, iph1->remote,
                                        "couldn't find the pskey for %s.\n",
                                        saddrwop2str(iph1->remote));
                                goto end;
                        }
                }
                plog(LLV_DEBUG, LOCATION, NULL, "the psk found.\n");
                /* should be secret PSK */
                plog(LLV_DEBUG2, LOCATION, NULL, "psk: ");
                plogdump(LLV_DEBUG2, iph1->authstr->v, iph1->authstr->l);

                len = iph1->nonce->l + iph1->nonce_p->l;
                buf = vmalloc(len);
                if (buf == NULL) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "failed to get skeyid buffer\n");
                        goto end;
                }
                p = buf->v;

                bp = (iph1->side == INITIATOR ? iph1->nonce : iph1->nonce_p);
                plog(LLV_DEBUG, LOCATION, NULL, "nonce 1: ");
                plogdump(LLV_DEBUG, bp->v, bp->l);
                memcpy(p, bp->v, bp->l);
                p += bp->l;

                bp = (iph1->side == INITIATOR ? iph1->nonce_p : iph1->nonce);
                plog(LLV_DEBUG, LOCATION, NULL, "nonce 2: ");
                plogdump(LLV_DEBUG, bp->v, bp->l);
                memcpy(p, bp->v, bp->l);
                p += bp->l;

                iph1->skeyid = oakley_prf(iph1->authstr, buf, iph1);
                if (iph1->skeyid == NULL)
                        goto end;
                break;

        case OAKLEY_ATTR_AUTH_METHOD_DSSSIG:
        case OAKLEY_ATTR_AUTH_METHOD_RSASIG:
#ifdef ENABLE_HYBRID
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I:
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I:
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R:
        case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I:
        case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R:
#endif
#ifdef HAVE_GSSAPI
        case OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB:
#endif
                len = iph1->nonce->l + iph1->nonce_p->l;
                buf = vmalloc(len);
                if (buf == NULL) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "failed to get nonce buffer\n");
                        goto end;
                }
                p = buf->v;

                bp = (iph1->side == INITIATOR ? iph1->nonce : iph1->nonce_p);
                plog(LLV_DEBUG, LOCATION, NULL, "nonce1: ");
                plogdump(LLV_DEBUG, bp->v, bp->l);
                memcpy(p, bp->v, bp->l);
                p += bp->l;

                bp = (iph1->side == INITIATOR ? iph1->nonce_p : iph1->nonce);
                plog(LLV_DEBUG, LOCATION, NULL, "nonce2: ");
                plogdump(LLV_DEBUG, bp->v, bp->l);
                memcpy(p, bp->v, bp->l);
                p += bp->l;

                iph1->skeyid = oakley_prf(buf, iph1->dhgxy, iph1);
                if (iph1->skeyid == NULL)
                        goto end;
                break;
        case OAKLEY_ATTR_AUTH_METHOD_RSAENC:
        case OAKLEY_ATTR_AUTH_METHOD_RSAREV:
#ifdef ENABLE_HYBRID
        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
                plog(LLV_WARNING, LOCATION, NULL,
                        "not supported authentication method %s\n",
                        s_oakley_attr_method(iph1->approval->authmethod));
                goto end;
        default:
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid authentication method %d\n",
                        iph1->approval->authmethod);
                goto end;
        }

        plog(LLV_DEBUG, LOCATION, NULL, "SKEYID computed:\n");
        plogdump(LLV_DEBUG, iph1->skeyid->v, iph1->skeyid->l);

        error = 0;

end:
        if (buf != NULL)
                vfree(buf);
        return error;
}

/*
 * compute SKEYID_[dae]
 * see seciton 5. Exchanges in RFC 2409
 * SKEYID_d = prf(SKEYID, g^ir | CKY-I | CKY-R | 0)
 * SKEYID_a = prf(SKEYID, SKEYID_d | g^ir | CKY-I | CKY-R | 1)
 * SKEYID_e = prf(SKEYID, SKEYID_a | g^ir | CKY-I | CKY-R | 2)
 */
int
oakley_skeyid_dae(struct ph1handle *iph1)
{
        vchar_t *buf = NULL;
        char *p;
        size_t len;
        int error = -1;

        if (iph1->skeyid == NULL) {
                plog(LLV_ERROR, LOCATION, NULL, "no SKEYID found.\n");
                goto end;
        }

        /* SKEYID D */
        /* SKEYID_d = prf(SKEYID, g^xy | CKY-I | CKY-R | 0) */
        len = iph1->dhgxy->l + sizeof(cookie_t) * 2 + 1;
        buf = vmalloc(len);
        if (buf == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get skeyid buffer\n");
                goto end;
        }
        p = buf->v;

        memcpy(p, iph1->dhgxy->v, iph1->dhgxy->l);
        p += iph1->dhgxy->l;
        memcpy(p, (caddr_t)&iph1->index.i_ck, sizeof(cookie_t));
        p += sizeof(cookie_t);
        memcpy(p, (caddr_t)&iph1->index.r_ck, sizeof(cookie_t));
        p += sizeof(cookie_t);
        *p = 0;
        iph1->skeyid_d = oakley_prf(iph1->skeyid, buf, iph1);
        if (iph1->skeyid_d == NULL)
                goto end;

        vfree(buf);
        buf = NULL;

        plog(LLV_DEBUG, LOCATION, NULL, "SKEYID_d computed:\n");
        plogdump(LLV_DEBUG, iph1->skeyid_d->v, iph1->skeyid_d->l);

        /* SKEYID A */
        /* SKEYID_a = prf(SKEYID, SKEYID_d | g^xy | CKY-I | CKY-R | 1) */
        len = iph1->skeyid_d->l + iph1->dhgxy->l + sizeof(cookie_t) * 2 + 1;
        buf = vmalloc(len);
        if (buf == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get skeyid buffer\n");
                goto end;
        }
        p = buf->v;
        memcpy(p, iph1->skeyid_d->v, iph1->skeyid_d->l);
        p += iph1->skeyid_d->l;
        memcpy(p, iph1->dhgxy->v, iph1->dhgxy->l);
        p += iph1->dhgxy->l;
        memcpy(p, (caddr_t)&iph1->index.i_ck, sizeof(cookie_t));
        p += sizeof(cookie_t);
        memcpy(p, (caddr_t)&iph1->index.r_ck, sizeof(cookie_t));
        p += sizeof(cookie_t);
        *p = 1;
        iph1->skeyid_a = oakley_prf(iph1->skeyid, buf, iph1);
        if (iph1->skeyid_a == NULL)
                goto end;

        vfree(buf);
        buf = NULL;

        plog(LLV_DEBUG, LOCATION, NULL, "SKEYID_a computed:\n");
        plogdump(LLV_DEBUG, iph1->skeyid_a->v, iph1->skeyid_a->l);

        /* SKEYID E */
        /* SKEYID_e = prf(SKEYID, SKEYID_a | g^xy | CKY-I | CKY-R | 2) */
        len = iph1->skeyid_a->l + iph1->dhgxy->l + sizeof(cookie_t) * 2 + 1;
        buf = vmalloc(len);
        if (buf == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get skeyid buffer\n");
                goto end;
        }
        p = buf->v;
        memcpy(p, iph1->skeyid_a->v, iph1->skeyid_a->l);
        p += iph1->skeyid_a->l;
        memcpy(p, iph1->dhgxy->v, iph1->dhgxy->l);
        p += iph1->dhgxy->l;
        memcpy(p, (caddr_t)&iph1->index.i_ck, sizeof(cookie_t));
        p += sizeof(cookie_t);
        memcpy(p, (caddr_t)&iph1->index.r_ck, sizeof(cookie_t));
        p += sizeof(cookie_t);
        *p = 2;
        iph1->skeyid_e = oakley_prf(iph1->skeyid, buf, iph1);
        if (iph1->skeyid_e == NULL)
                goto end;

        vfree(buf);
        buf = NULL;

        plog(LLV_DEBUG, LOCATION, NULL, "SKEYID_e computed:\n");
        plogdump(LLV_DEBUG, iph1->skeyid_e->v, iph1->skeyid_e->l);

        error = 0;

end:
        if (buf != NULL)
                vfree(buf);
        return error;
}

/*
 * compute final encryption key.
 * see Appendix B.
 */
int
oakley_compute_enckey(struct ph1handle *iph1)
{
        u_int keylen, prflen;
        int error = -1;

        /* RFC2409 p39 */
        keylen = alg_oakley_encdef_keylen(iph1->approval->enctype,
                                        iph1->approval->encklen);
        if (keylen == (u_int)-1) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid encryption algorithm %d, "
                        "or invalid key length %d.\n",
                        iph1->approval->enctype,
                        iph1->approval->encklen);
                goto end;
        }
        iph1->key = vmalloc(keylen >> 3);
        if (iph1->key == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get key buffer\n");
                goto end;
        }

        /* set prf length */
        prflen = alg_oakley_hashdef_hashlen(iph1->approval->hashtype);
        if (prflen == (u_int)-1) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid hash type %d.\n", iph1->approval->hashtype);
                goto end;
        }

        /* see isakmp-oakley-08 5.3. */
        if (iph1->key->l <= iph1->skeyid_e->l) {
                /*
                 * if length(Ka) <= length(SKEYID_e)
                 *      Ka = first length(K) bit of SKEYID_e
                 */
                memcpy(iph1->key->v, iph1->skeyid_e->v, iph1->key->l);
        } else {
                vchar_t *buf = NULL, *res = NULL;
                u_char *p, *ep;
                int cplen;
                int subkey;

                /*
                 * otherwise,
                 *      Ka = K1 | K2 | K3
                 * where
                 *      K1 = prf(SKEYID_e, 0)
                 *      K2 = prf(SKEYID_e, K1)
                 *      K3 = prf(SKEYID_e, K2)
                 */
                plog(LLV_DEBUG, LOCATION, NULL,
                        "len(SKEYID_e) < len(Ka) (%zu < %zu), "
                        "generating long key (Ka = K1 | K2 | ...)\n",
                        iph1->skeyid_e->l, iph1->key->l);

                if ((buf = vmalloc(prflen >> 3)) == 0) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "failed to get key buffer\n");
                        goto end;
                }
                p = (u_char *)iph1->key->v;
                ep = p + iph1->key->l;

                subkey = 1;
                while (p < ep) {
                        if (p == (u_char *)iph1->key->v) {
                                /* just for computing K1 */
                                buf->v[0] = 0;
                                buf->l = 1;
                        }
                        res = oakley_prf(iph1->skeyid_e, buf, iph1);
                        if (res == NULL) {
                                vfree(buf);
                                goto end;
                        }
                        plog(LLV_DEBUG, LOCATION, NULL,
                                "compute intermediate encryption key K%d\n",
                                subkey);
                        plogdump(LLV_DEBUG, buf->v, buf->l);
                        plogdump(LLV_DEBUG, res->v, res->l);

                        cplen = (res->l < ep - p) ? res->l : ep - p;
                        memcpy(p, res->v, cplen);
                        p += cplen;

                        buf->l = prflen >> 3;   /* to cancel K1 speciality */
                        if (res->l != buf->l) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                        "internal error: res->l=%zu buf->l=%zu\n",
                                        res->l, buf->l);
                                vfree(res);
                                vfree(buf);
                                goto end;
                        }
                        memcpy(buf->v, res->v, res->l);
                        vfree(res);
                        subkey++;
                }

                vfree(buf);
        }

        /*
         * don't check any weak key or not.
         * draft-ietf-ipsec-ike-01.txt Appendix B.
         * draft-ietf-ipsec-ciph-aes-cbc-00.txt Section 2.3.
         */
#if 0
        /* weakkey check */
        if (iph1->approval->enctype > ARRAYLEN(oakley_encdef)
         || oakley_encdef[iph1->approval->enctype].weakkey == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "encryption algorithm %d isn't supported.\n",
                        iph1->approval->enctype);
                goto end;
        }
        if ((oakley_encdef[iph1->approval->enctype].weakkey)(iph1->key)) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "weakkey was generated.\n");
                goto end;
        }
#endif

        plog(LLV_DEBUG, LOCATION, NULL, "final encryption key computed:\n");
        plogdump(LLV_DEBUG, iph1->key->v, iph1->key->l);

        error = 0;

end:
        return error;
}

/*
 * compute IV and set to ph1handle
 *      IV = hash(g^xi | g^xr)
 * see 4.1 Phase 1 state in draft-ietf-ipsec-ike.
 */
int
oakley_newiv(struct ph1handle *iph1)
{
        struct isakmp_ivm *newivm = NULL;
        vchar_t *buf = NULL, *bp;
        char *p;
        size_t len;

        /* create buffer */
        len = iph1->dhpub->l + iph1->dhpub_p->l;
        buf = vmalloc(len);
        if (buf == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get iv buffer\n");
                return -1;
        }

        p = buf->v;

        bp = (iph1->side == INITIATOR ? iph1->dhpub : iph1->dhpub_p);
        memcpy(p, bp->v, bp->l);
        p += bp->l;

        bp = (iph1->side == INITIATOR ? iph1->dhpub_p : iph1->dhpub);
        memcpy(p, bp->v, bp->l);
        p += bp->l;

        /* allocate IVm */
        newivm = racoon_calloc(1, sizeof(struct isakmp_ivm));
        if (newivm == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get iv buffer\n");
                vfree(buf);
                return -1;
        }

        /* compute IV */
        newivm->iv = oakley_hash(buf, iph1);
        if (newivm->iv == NULL) {
                vfree(buf);
                oakley_delivm(newivm);
                return -1;
        }

        /* adjust length of iv */
        newivm->iv->l = alg_oakley_encdef_blocklen(iph1->approval->enctype);
        if (newivm->iv->l == (u_int)-1) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid encryption algorithm %d.\n",
                        iph1->approval->enctype);
                vfree(buf);
                oakley_delivm(newivm);
                return -1;
        }

        /* create buffer to save iv */
        if ((newivm->ive = vdup(newivm->iv)) == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "vdup (%s)\n", strerror(errno));
                vfree(buf);
                oakley_delivm(newivm);
                return -1;
        }

        vfree(buf);

        plog(LLV_DEBUG, LOCATION, NULL, "IV computed:\n");
        plogdump(LLV_DEBUG, newivm->iv->v, newivm->iv->l);

        iph1->ivm = newivm;

        return 0;
}

/*
 * compute IV for the payload after phase 1.
 * It's not limited for phase 2.
 * if pahse 1 was encrypted.
 *      IV = hash(last CBC block of Phase 1 | M-ID)
 * if phase 1 was not encrypted.
 *      IV = hash(phase 1 IV | M-ID)
 * see 4.2 Phase 2 state in draft-ietf-ipsec-ike.
 */
struct isakmp_ivm *
oakley_newiv2(struct ph1handle *iph1, uint32_t msgid)
{
        struct isakmp_ivm *newivm = NULL;
        vchar_t *buf = NULL;
        char *p;
        size_t len;
        int error = -1;

        /* create buffer */
        len = iph1->ivm->iv->l + sizeof(msgid_t);
        buf = vmalloc(len);
        if (buf == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get iv buffer\n");
                goto end;
        }

        p = buf->v;

        memcpy(p, iph1->ivm->iv->v, iph1->ivm->iv->l);
        p += iph1->ivm->iv->l;

        memcpy(p, &msgid, sizeof(msgid));

        plog(LLV_DEBUG, LOCATION, NULL, "compute IV for phase2\n");
        plog(LLV_DEBUG, LOCATION, NULL, "phase1 last IV:\n");
        plogdump(LLV_DEBUG, buf->v, buf->l);

        /* allocate IVm */
        newivm = racoon_calloc(1, sizeof(struct isakmp_ivm));
        if (newivm == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get iv buffer\n");
                goto end;
        }

        /* compute IV */
        if ((newivm->iv = oakley_hash(buf, iph1)) == NULL)
                goto end;

        /* adjust length of iv */
        newivm->iv->l = alg_oakley_encdef_blocklen(iph1->approval->enctype);
        if (newivm->iv->l == (u_int)-1) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid encryption algorithm %d.\n",
                        iph1->approval->enctype);
                goto end;
        }

        /* create buffer to save new iv */
        if ((newivm->ive = vdup(newivm->iv)) == NULL) {
                plog(LLV_ERROR, LOCATION, NULL, "vdup (%s)\n", strerror(errno));
                goto end;
        }

        error = 0;

        plog(LLV_DEBUG, LOCATION, NULL, "phase2 IV computed:\n");
        plogdump(LLV_DEBUG, newivm->iv->v, newivm->iv->l);

end:
        if (error && newivm != NULL){
                oakley_delivm(newivm);
                newivm=NULL;
        }
        if (buf != NULL)
                vfree(buf);
        return newivm;
}

void
oakley_delivm(struct isakmp_ivm *ivm)
{
        if (ivm == NULL)
                return;

        if (ivm->iv != NULL)
                vfree(ivm->iv);
        if (ivm->ive != NULL)
                vfree(ivm->ive);
        racoon_free(ivm);
        plog(LLV_DEBUG, LOCATION, NULL, "IV freed\n");

        return;
}

/*
 * decrypt packet.
 *   save new iv and old iv.
 */
vchar_t *
oakley_do_decrypt(struct ph1handle *iph1, vchar_t *msg, vchar_t *ivdp,
    vchar_t *ivep)
{
        vchar_t *buf = NULL, *new = NULL;
        char *pl;
        size_t len;
        uint8_t padlen;
        int blen;
        int error = -1;

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

        blen = alg_oakley_encdef_blocklen(iph1->approval->enctype);
        if (blen == -1) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid encryption algorithm %d.\n",
                        iph1->approval->enctype);
                goto end;
        }

        /* save IV for next, but not sync. */
        memset(ivep->v, 0, ivep->l);
        memcpy(ivep->v, (caddr_t)&msg->v[msg->l - blen], blen);

        plog(LLV_DEBUG, LOCATION, NULL,
                "IV was saved for next processing:\n");
        plogdump(LLV_DEBUG, ivep->v, ivep->l);

        pl = msg->v + sizeof(struct isakmp);

        len = msg->l - sizeof(struct isakmp);

        /* create buffer */
        buf = vmalloc(len);
        if (buf == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get buffer to decrypt.\n");
                goto end;
        }
        memcpy(buf->v, pl, len);

        /* do decrypt */
        new = alg_oakley_encdef_decrypt(iph1->approval->enctype,
                                        buf, iph1->key, ivdp);
        if (new == NULL || new->v == NULL || new->l == 0) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "decryption %d failed.\n", iph1->approval->enctype);
                goto end;
        }
        plog(LLV_DEBUG, LOCATION, NULL, "with key:\n");
        plogdump(LLV_DEBUG, iph1->key->v, iph1->key->l);

        vfree(buf);
        buf = NULL;

        plog(LLV_DEBUG, LOCATION, NULL, "decrypted payload by IV:\n");
        plogdump(LLV_DEBUG, ivdp->v, ivdp->l);

        plog(LLV_DEBUG, LOCATION, NULL,
                "decrypted payload, but not trimed.\n");
        plogdump(LLV_DEBUG, new->v, new->l);

        /* get padding length */
        if (lcconf->pad_excltail)
                padlen = new->v[new->l - 1] + 1;
        else
                padlen = new->v[new->l - 1];
        plog(LLV_DEBUG, LOCATION, NULL, "padding len=%u\n", padlen);

        /* trim padding */
        if (lcconf->pad_strict) {
                if (padlen > new->l) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalied padding len=%u, buflen=%zu.\n",
                                padlen, new->l);
                        plogdump(LLV_ERROR, new->v, new->l);
                        goto end;
                }
                new->l -= padlen;
                plog(LLV_DEBUG, LOCATION, NULL, "trimmed padding\n");
        } else {
                plog(LLV_DEBUG, LOCATION, NULL, "skip to trim padding.\n");
        }

        /* create new buffer */
        len = sizeof(struct isakmp) + new->l;
        buf = vmalloc(len);
        if (buf == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get buffer to decrypt.\n");
                goto end;
        }
        memcpy(buf->v, msg->v, sizeof(struct isakmp));
        memcpy(buf->v + sizeof(struct isakmp), new->v, new->l);
        ((struct isakmp *)buf->v)->len = htonl(buf->l);

        plog(LLV_DEBUG, LOCATION, NULL, "decrypted.\n");
        plogdump(LLV_DEBUG, buf->v, buf->l);

#ifdef HAVE_PRINT_ISAKMP_C
        isakmp_printpacket(buf, iph1->remote, iph1->local, 1);
#endif

        error = 0;

end:
        if (error && buf != NULL) {
                vfree(buf);
                buf = NULL;
        }
        if (new != NULL)
                vfree(new);

        return buf;
}

/*
 * encrypt packet.
 */
vchar_t *
oakley_do_encrypt(struct ph1handle *iph1, vchar_t *msg, vchar_t *ivep,
    vchar_t *ivp)
{
        vchar_t *buf = 0, *new = 0;
        char *pl;
        size_t len;
        u_int padlen;
        int blen;
        int error = -1;

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

        /* set cbc block length */
        blen = alg_oakley_encdef_blocklen(iph1->approval->enctype);
        if (blen == -1) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "invalid encryption algorithm %d.\n",
                        iph1->approval->enctype);
                goto end;
        }

        pl = msg->v + sizeof(struct isakmp);
        len = msg->l - sizeof(struct isakmp);

        /* add padding */
        padlen = oakley_padlen(len, blen);
        plog(LLV_DEBUG, LOCATION, NULL, "pad length = %u\n", padlen);

        /* create buffer */
        buf = vmalloc(len + padlen);
        if (buf == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get buffer to encrypt.\n");
                goto end;
        }
        if (padlen) {
                int i;
                char *p = &buf->v[len];
                if (lcconf->pad_random) {
                        for (i = 0; i < padlen; i++)
                                *p++ = eay_random() & 0xff;
                }
        }
        memcpy(buf->v, pl, len);

        /* make pad into tail */
        if (lcconf->pad_excltail)
                buf->v[len + padlen - 1] = padlen - 1;
        else
                buf->v[len + padlen - 1] = padlen;

        plogdump(LLV_DEBUG, buf->v, buf->l);

        /* do encrypt */
        new = alg_oakley_encdef_encrypt(iph1->approval->enctype,
                                        buf, iph1->key, ivep);
        if (new == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "encryption %d failed.\n", iph1->approval->enctype);
                goto end;
        }
        plog(LLV_DEBUG, LOCATION, NULL, "with key:\n");
        plogdump(LLV_DEBUG, iph1->key->v, iph1->key->l);

        vfree(buf);
        buf = NULL;

        plog(LLV_DEBUG, LOCATION, NULL, "encrypted payload by IV:\n");
        plogdump(LLV_DEBUG, ivep->v, ivep->l);

        /* save IV for next */
        memset(ivp->v, 0, ivp->l);
        memcpy(ivp->v, (caddr_t)&new->v[new->l - blen], blen);

        plog(LLV_DEBUG, LOCATION, NULL, "save IV for next:\n");
        plogdump(LLV_DEBUG, ivp->v, ivp->l);

        /* create new buffer */
        len = sizeof(struct isakmp) + new->l;
        buf = vmalloc(len);
        if (buf == NULL) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "failed to get buffer to encrypt.\n");
                goto end;
        }
        memcpy(buf->v, msg->v, sizeof(struct isakmp));
        memcpy(buf->v + sizeof(struct isakmp), new->v, new->l);
        ((struct isakmp *)buf->v)->len = htonl(buf->l);

        error = 0;

        plog(LLV_DEBUG, LOCATION, NULL, "encrypted.\n");

end:
        if (error && buf != NULL) {
                vfree(buf);
                buf = NULL;
        }
        if (new != NULL)
                vfree(new);

        return buf;
}

/* culculate padding length */
static int
oakley_padlen(int len, int base)
{
        int padlen;

        padlen = base - len % base;

        if (lcconf->pad_randomlen)
                padlen += ((eay_random() % (lcconf->pad_maxsize + 1) + 1) *
                    base);

        return padlen;
}