diff options
Diffstat (limited to 'ipsec-tools/src/racoon/oakley.c')
-rw-r--r-- | ipsec-tools/src/racoon/oakley.c | 3211 |
1 files changed, 3211 insertions, 0 deletions
diff --git a/ipsec-tools/src/racoon/oakley.c b/ipsec-tools/src/racoon/oakley.c new file mode 100644 index 00000000..cad68744 --- /dev/null +++ b/ipsec-tools/src/racoon/oakley.c @@ -0,0 +1,3211 @@ +/* $NetBSD: oakley.c,v 1.22.2.2 2012/08/29 11:35:09 tteras 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 __P((vchar_t *, vchar_t **)); +static int oakley_compute_keymat_x __P((struct ph2handle *, int, int)); +static int oakley_check_certid __P((struct ph1handle *iph1)); +static int check_typeofcertname __P((int, int)); +static int oakley_padlen __P((int, int)); +static int get_plainrsa_fromlocal __P((struct ph1handle *, int)); + +int oakley_get_certtype(cert) + vchar_t *cert; +{ + if (cert == NULL) + return ISAKMP_CERT_NONE; + + return cert->v[0]; +} + +static vchar_t * +dump_isakmp_payload(gen) + 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(cert) + 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(dhgrp) + 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(prime, pub0) + vchar_t *prime, **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(dh, pub, priv, pub_p, gxy) + const struct dhgroup *dh; + vchar_t *pub, *priv, *pub_p, **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(dh, pub, priv) + const struct dhgroup *dh; + vchar_t **pub, **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(group, dhgrp) + 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 vlaues */ + 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(key, buf, iph1) + vchar_t *key, *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(buf, iph1) + 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(iph2, side) + 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(iph2, side, sa_dir) + struct ph2handle *iph2; + int side; + int sa_dir; +{ + vchar_t *buf = NULL, *res = NULL, *bp; + char *p; + int len; + int error = -1; + int pfs = 0; + int dupkeymat; /* generate K[1-dupkeymat] */ + struct saproto *pr; + struct satrns *tr; + int encklen, authklen, l; + + pfs = ((iph2->approval->pfs_group && iph2->dhgxy) ? 1 : 0); + + len = pfs ? iph2->dhgxy->l : 0; + len += (1 + + sizeof(u_int32_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) { + l = alg_ipsec_encdef_keylen(tr->trns_id, + tr->encklen); + if (l > encklen) + encklen = l; + + l = alg_ipsec_hmacdef_hashlen(tr->authtype); + if (l > authklen) + authklen = l; + } + break; + case IPSECDOI_PROTO_IPSEC_AH: + for (tr = pr->head; tr; tr = tr->next) { + l = alg_ipsec_hmacdef_hashlen(tr->trns_id); + if (l > authklen) + authklen = l; + } + 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(iph1, msgid, body) + struct ph1handle *iph1; + u_int32_t msgid; + vchar_t *body; +{ + vchar_t *buf = 0, *res = 0; + int len; + int error = -1; + + /* create buffer */ + len = 1 + sizeof(u_int32_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(u_int32_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; + + error = 0; + + 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(iph1, msgid, body) + struct ph1handle *iph1; + u_int32_t msgid; + vchar_t *body; +{ + vchar_t *buf = NULL, *res = NULL; + char *p; + int len; + int error = -1; + + /* create buffer */ + len = sizeof(u_int32_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(u_int32_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; + + error = 0; + + 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(iph1, sw) + struct ph1handle *iph1; + int sw; +{ + vchar_t *buf = NULL, *res = NULL, *bp; + char *p, *bp2; + int len, bl; + int error = -1; +#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; + + error = 0; + + 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(iph1, sw) + 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; + int len; + int error = -1; + + /* 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; + + error = 0; + + 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(iph1, sw) + struct ph1handle *iph1; + int sw; +{ + vchar_t *buf = NULL, *res = NULL, *bp; + vchar_t *hash = NULL; + char *p; + int len; + int error = -1; + + /* 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; + break; + } + + /* 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; + + error = 0; + + 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(iph1) + 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; + break; + } +#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; +#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; + break; + } + plog(LLV_INFO, LOCATION, NULL, "No SIG was passed, " + "but hybrid auth is enabled\n"); + + return 0; + break; + } +#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(iph1) + 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(iph1, my) + struct ph1handle *iph1; + int my; +{ + char path[MAXPATHLEN]; + vchar_t *cert = NULL; + char *certfile; + 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(iph1) + 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(iph1) + 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(doi, genid) + int doi, 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(iph1, gen) + struct ph1handle *iph1; + struct isakmp_gen *gen; +{ + vchar_t **c; + u_int8_t type; + STACK_OF(X509) *certs=NULL; + PKCS7 *p7; + + type = *(u_int8_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++) { + int len; + u_char *bp; + 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(iph1, gen) + struct ph1handle *iph1; + struct isakmp_gen *gen; +{ + vchar_t *cert; + vchar_t **c; + u_int8_t type; + + type = *(u_int8_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(rmconf, ctx) + 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(plist, iph1) + 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(iph1) + 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(type) + 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(iph1) + struct ph1handle *iph1; +{ + vchar_t *buf = NULL, *bp; + char *p; + int 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(iph1) + struct ph1handle *iph1; +{ + vchar_t *buf = NULL; + char *p; + int 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(iph1) + 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 == -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 == -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(iph1) + struct ph1handle *iph1; +{ + struct isakmp_ivm *newivm = NULL; + vchar_t *buf = NULL, *bp; + char *p; + int 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 == -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(iph1, msgid) + struct ph1handle *iph1; + u_int32_t msgid; +{ + struct isakmp_ivm *newivm = NULL; + vchar_t *buf = NULL; + char *p; + int 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 == -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(ivm) + 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(iph1, msg, ivdp, ivep) + struct ph1handle *iph1; + vchar_t *msg, *ivdp, *ivep; +{ + vchar_t *buf = NULL, *new = NULL; + char *pl; + int len; + u_int8_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(iph1, msg, ivep, ivp) + struct ph1handle *iph1; + vchar_t *msg, *ivep, *ivp; +{ + vchar_t *buf = 0, *new = 0; + char *pl; + int 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(len, base) + int len, base; +{ + int padlen; + + padlen = base - len % base; + + if (lcconf->pad_randomlen) + padlen += ((eay_random() % (lcconf->pad_maxsize + 1) + 1) * + base); + + return padlen; +} + |