summaryrefslogtreecommitdiffstats
path: root/ipsec-tools/src/racoon/oakley.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipsec-tools/src/racoon/oakley.c')
-rw-r--r--ipsec-tools/src/racoon/oakley.c3211
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;
+}
+