summaryrefslogtreecommitdiffstats
path: root/ipsec-tools/src/racoon/ipsec_doi.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipsec-tools/src/racoon/ipsec_doi.c')
-rw-r--r--ipsec-tools/src/racoon/ipsec_doi.c4796
1 files changed, 4796 insertions, 0 deletions
diff --git a/ipsec-tools/src/racoon/ipsec_doi.c b/ipsec-tools/src/racoon/ipsec_doi.c
new file mode 100644
index 00000000..84a4c715
--- /dev/null
+++ b/ipsec-tools/src/racoon/ipsec_doi.c
@@ -0,0 +1,4796 @@
+/* $NetBSD: ipsec_doi.c,v 1.46.4.1 2013/06/18 05:40:36 tteras Exp $ */
+
+/* Id: ipsec_doi.c,v 1.55 2006/08/17 09:20:41 vanhu Exp */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include PATH_IPSEC_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#include "var.h"
+#include "vmbuf.h"
+#include "misc.h"
+#include "plog.h"
+#include "debug.h"
+
+#include "cfparse_proto.h"
+#include "isakmp_var.h"
+#include "isakmp.h"
+#include "ipsec_doi.h"
+#include "oakley.h"
+#include "remoteconf.h"
+#include "localconf.h"
+#include "sockmisc.h"
+#include "handler.h"
+#include "policy.h"
+#include "algorithm.h"
+#include "sainfo.h"
+#include "proposal.h"
+#include "crypto_openssl.h"
+#include "strnames.h"
+#include "gcmalloc.h"
+
+#ifdef ENABLE_NATT
+#include "nattraversal.h"
+#endif
+
+#ifdef HAVE_GSSAPI
+#include <iconv.h>
+#include "gssapi.h"
+#ifdef HAVE_ICONV_2ND_CONST
+#define __iconv_const const
+#else
+#define __iconv_const
+#endif
+#endif
+
+static vchar_t *get_ph1approval __P((struct ph1handle *, u_int32_t, u_int32_t,
+ struct prop_pair **));
+static int get_ph1approvalx __P((struct remoteconf *, void *));
+
+static int t2isakmpsa __P((struct isakmp_pl_t *, struct isakmpsa *, u_int32_t));
+static int cmp_aproppair_i __P((struct prop_pair *, struct prop_pair *));
+static struct prop_pair *get_ph2approval __P((struct ph2handle *,
+ struct prop_pair **));
+static struct prop_pair *get_ph2approvalx __P((struct ph2handle *,
+ struct prop_pair *));
+static void free_proppair0 __P((struct prop_pair *));
+static struct prop_pair ** get_proppair_and_doi_sit __P((vchar_t *, int,
+ u_int32_t *, u_int32_t *));
+
+static int get_transform
+ __P((struct isakmp_pl_p *, struct prop_pair **, int *));
+static u_int32_t ipsecdoi_set_ld __P((vchar_t *));
+
+static int check_doi __P((u_int32_t));
+static int check_situation __P((u_int32_t));
+
+static int check_prot_main __P((int));
+static int check_prot_quick __P((int));
+static int (*check_protocol[]) __P((int)) = {
+ check_prot_main, /* IPSECDOI_TYPE_PH1 */
+ check_prot_quick, /* IPSECDOI_TYPE_PH2 */
+};
+
+static int check_spi_size __P((int, int));
+
+static int check_trns_isakmp __P((int));
+static int check_trns_ah __P((int));
+static int check_trns_esp __P((int));
+static int check_trns_ipcomp __P((int));
+static int (*check_transform[]) __P((int)) = {
+ 0,
+ check_trns_isakmp, /* IPSECDOI_PROTO_ISAKMP */
+ check_trns_ah, /* IPSECDOI_PROTO_IPSEC_AH */
+ check_trns_esp, /* IPSECDOI_PROTO_IPSEC_ESP */
+ check_trns_ipcomp, /* IPSECDOI_PROTO_IPCOMP */
+};
+
+static int check_attr_isakmp __P((struct isakmp_pl_t *));
+static int check_attr_ah __P((struct isakmp_pl_t *));
+static int check_attr_esp __P((struct isakmp_pl_t *));
+static int check_attr_ipsec __P((int, struct isakmp_pl_t *));
+static int check_attr_ipcomp __P((struct isakmp_pl_t *));
+static int (*check_attributes[]) __P((struct isakmp_pl_t *)) = {
+ 0,
+ check_attr_isakmp, /* IPSECDOI_PROTO_ISAKMP */
+ check_attr_ah, /* IPSECDOI_PROTO_IPSEC_AH */
+ check_attr_esp, /* IPSECDOI_PROTO_IPSEC_ESP */
+ check_attr_ipcomp, /* IPSECDOI_PROTO_IPCOMP */
+};
+
+static int setph1prop __P((struct isakmpsa *, caddr_t));
+static int setph1trns __P((struct isakmpsa *, caddr_t));
+static int setph1attr __P((struct isakmpsa *, caddr_t));
+static vchar_t *setph2proposal0 __P((const struct ph2handle *,
+ const struct saprop *, const struct saproto *));
+
+struct ph1approvalx_ctx {
+ struct prop_pair *p;
+ struct isakmpsa *sa;
+};
+
+/*%%%*/
+/*
+ * check phase 1 SA payload.
+ * make new SA payload to be replyed not including general header.
+ * the pointer to one of isakmpsa in proposal is set into iph1->approval.
+ * OUT:
+ * positive: the pointer to new buffer of SA payload.
+ * network byte order.
+ * NULL : error occurd.
+ */
+int
+ipsecdoi_checkph1proposal(sa, iph1)
+ vchar_t *sa;
+ struct ph1handle *iph1;
+{
+ vchar_t *newsa; /* new SA payload approved. */
+ struct prop_pair **pair;
+ u_int32_t doitype, sittype;
+
+ /* get proposal pair */
+ pair = get_proppair_and_doi_sit(sa, IPSECDOI_TYPE_PH1,
+ &doitype, &sittype);
+ if (pair == NULL)
+ return -1;
+
+ /* check and get one SA for use */
+ newsa = get_ph1approval(iph1, doitype, sittype, pair);
+ free_proppair(pair);
+
+ if (newsa == NULL)
+ return -1;
+
+ iph1->sa_ret = newsa;
+ return 0;
+}
+
+static void
+print_ph1proposal(pair, s)
+ struct prop_pair *pair;
+ struct isakmpsa *s;
+{
+ struct isakmp_pl_p *prop = pair->prop;
+ struct isakmp_pl_t *trns = pair->trns;
+
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "prop#=%d, prot-id=%s, spi-size=%d, #trns=%d\n",
+ prop->p_no, s_ipsecdoi_proto(prop->proto_id),
+ prop->spi_size, prop->num_t);
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "trns#=%d, trns-id=%s\n",
+ trns->t_no, s_ipsecdoi_trns(prop->proto_id, trns->t_id));
+ plog(LLV_DEBUG, LOCATION, NULL,
+ " lifetime = %ld\n", (long) s->lifetime);
+ plog(LLV_DEBUG, LOCATION, NULL,
+ " lifebyte = %zu\n", s->lifebyte);
+ plog(LLV_DEBUG, LOCATION, NULL,
+ " enctype = %s\n",
+ s_oakley_attr_v(OAKLEY_ATTR_ENC_ALG, s->enctype));
+ plog(LLV_DEBUG, LOCATION, NULL,
+ " encklen = %d\n", s->encklen);
+ plog(LLV_DEBUG, LOCATION, NULL,
+ " hashtype = %s\n",
+ s_oakley_attr_v(OAKLEY_ATTR_HASH_ALG, s->hashtype));
+ plog(LLV_DEBUG, LOCATION, NULL,
+ " authmethod = %s\n",
+ s_oakley_attr_v(OAKLEY_ATTR_AUTH_METHOD, s->authmethod));
+ plog(LLV_DEBUG, LOCATION, NULL,
+ " dh_group = %s\n",
+ s_oakley_attr_v(OAKLEY_ATTR_GRP_DESC, s->dh_group));
+}
+
+
+/*
+ * acceptable check for remote configuration.
+ * return a new SA payload to be reply to peer.
+ */
+
+static vchar_t *
+get_ph1approval(iph1, doitype, sittype, pair)
+ struct ph1handle *iph1;
+ u_int32_t doitype, sittype;
+ struct prop_pair **pair;
+{
+ vchar_t *newsa;
+ struct ph1approvalx_ctx ctx;
+ struct prop_pair *s, *p;
+ struct rmconfselector rmsel;
+ struct isakmpsa *sa;
+ int i;
+
+ memset(&rmsel, 0, sizeof(rmsel));
+ rmsel.remote = iph1->remote;
+
+ if (iph1->approval) {
+ delisakmpsa(iph1->approval);
+ iph1->approval = NULL;
+ }
+
+ for (i = 0; i < MAXPROPPAIRLEN; i++) {
+ if (pair[i] == NULL)
+ continue;
+ for (s = pair[i]; s; s = s->next) {
+ /* compare proposal and select one */
+ for (p = s; p; p = p->tnext) {
+ struct isakmp_pl_p *prop = p->prop;
+
+ sa = newisakmpsa();
+ ctx.p = p;
+ ctx.sa = sa;
+ if (t2isakmpsa(p->trns, sa,
+ iph1->vendorid_mask) < 0)
+ continue;
+ print_ph1proposal(p, sa);
+ if (iph1->rmconf != NULL) {
+ if (get_ph1approvalx(iph1->rmconf, &ctx))
+ goto found;
+ } else {
+ if (enumrmconf(&rmsel, get_ph1approvalx, &ctx))
+ goto found;
+ }
+ delisakmpsa(sa);
+ }
+ }
+ }
+
+ plog(LLV_ERROR, LOCATION, NULL, "no suitable proposal found.\n");
+
+ return NULL;
+
+found:
+ sa = ctx.sa;
+ plog(LLV_DEBUG, LOCATION, NULL, "an acceptable proposal found.\n");
+
+ /* check DH group settings */
+ if (sa->dhgrp) {
+ if (sa->dhgrp->prime && sa->dhgrp->gen1) {
+ /* it's ok */
+ goto saok;
+ }
+ plog(LLV_WARNING, LOCATION, NULL,
+ "invalid DH parameter found, use default.\n");
+ oakley_dhgrp_free(sa->dhgrp);
+ sa->dhgrp=NULL;
+ }
+
+ if (oakley_setdhgroup(sa->dh_group, &sa->dhgrp) == -1) {
+ sa->dhgrp = NULL;
+ delisakmpsa(sa);
+ return NULL;
+ }
+
+saok:
+#ifdef HAVE_GSSAPI
+ if (sa->gssid != NULL)
+ plog(LLV_DEBUG, LOCATION, NULL, "gss id in new sa '%.*s'\n",
+ (int)sa->gssid->l, sa->gssid->v);
+ if (iph1->side == INITIATOR) {
+ if (iph1->rmconf->proposal->gssid != NULL)
+ iph1->gi_i = vdup(iph1->rmconf->proposal->gssid);
+ if (sa->gssid != NULL)
+ iph1->gi_r = vdup(sa->gssid);
+ } else {
+ if (sa->gssid != NULL) {
+ iph1->gi_r = vdup(sa->gssid);
+ iph1->gi_i = gssapi_get_id(iph1);
+ }
+ }
+ if (iph1->gi_i != NULL)
+ plog(LLV_DEBUG, LOCATION, NULL, "GIi is %.*s\n",
+ (int)iph1->gi_i->l, iph1->gi_i->v);
+ if (iph1->gi_r != NULL)
+ plog(LLV_DEBUG, LOCATION, NULL, "GIr is %.*s\n",
+ (int)iph1->gi_r->l, iph1->gi_r->v);
+#endif
+ plog(LLV_DEBUG, LOCATION, NULL, "agreed on %s auth.\n",
+ s_oakley_attr_method(sa->authmethod));
+
+ newsa = get_sabyproppair(doitype, sittype, p);
+ if (newsa == NULL)
+ delisakmpsa(sa);
+ else
+ iph1->approval = sa;
+
+ return newsa;
+}
+
+/*
+ * compare peer's single proposal and all of my proposal.
+ * and select one if suiatable.
+ */
+static int
+get_ph1approvalx(rmconf, ctx)
+ struct remoteconf *rmconf;
+ void *ctx;
+{
+ struct ph1approvalx_ctx *pctx = (struct ph1approvalx_ctx *) ctx;
+ struct isakmpsa *sa;
+
+ /* do the hard work */
+ sa = checkisakmpsa(rmconf->pcheck_level, pctx->sa, rmconf->proposal);
+ if (sa == NULL)
+ return 0;
+
+ /* duplicate and modify the found SA to match proposal */
+ sa = dupisakmpsa(sa);
+
+ switch (rmconf->pcheck_level) {
+ case PROP_CHECK_OBEY:
+ sa->lifetime = pctx->sa->lifetime;
+ sa->lifebyte = pctx->sa->lifebyte;
+ sa->encklen = pctx->sa->encklen;
+ break;
+ case PROP_CHECK_CLAIM:
+ case PROP_CHECK_STRICT:
+ if (pctx->sa->lifetime < sa->lifetime)
+ sa->lifetime = pctx->sa->lifetime;
+ if (pctx->sa->lifebyte < sa->lifebyte)
+ sa->lifebyte = pctx->sa->lifebyte;
+ if (pctx->sa->encklen > sa->encklen)
+ sa->encklen = pctx->sa->encklen;
+ break;
+ default:
+ break;
+ }
+
+ /* replace the proposal with our approval sa */
+ delisakmpsa(pctx->sa);
+ pctx->sa = sa;
+
+ return 1;
+}
+
+/*
+ * get ISAKMP data attributes
+ */
+static int
+t2isakmpsa(trns, sa, vendorid_mask)
+ struct isakmp_pl_t *trns;
+ struct isakmpsa *sa;
+ u_int32_t vendorid_mask;
+{
+ struct isakmp_data *d, *prev;
+ int flag, type;
+ int error = -1;
+ int life_t;
+ int keylen = 0;
+ vchar_t *val = NULL;
+ int len, tlen;
+ u_char *p;
+
+ tlen = ntohs(trns->h.len) - sizeof(*trns);
+ prev = (struct isakmp_data *)NULL;
+ d = (struct isakmp_data *)(trns + 1);
+
+ /* default */
+ life_t = OAKLEY_ATTR_SA_LD_TYPE_DEFAULT;
+ sa->lifetime = OAKLEY_ATTR_SA_LD_SEC_DEFAULT;
+ sa->lifebyte = 0;
+ sa->dhgrp = racoon_calloc(1, sizeof(struct dhgroup));
+ if (!sa->dhgrp)
+ goto err;
+
+ while (tlen > 0) {
+
+ type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
+ flag = ntohs(d->type) & ISAKMP_GEN_MASK;
+
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "type=%s, flag=0x%04x, lorv=%s\n",
+ s_oakley_attr(type), flag,
+ s_oakley_attr_v(type, ntohs(d->lorv)));
+
+ /* get variable-sized item */
+ switch (type) {
+ case OAKLEY_ATTR_GRP_PI:
+ case OAKLEY_ATTR_GRP_GEN_ONE:
+ case OAKLEY_ATTR_GRP_GEN_TWO:
+ case OAKLEY_ATTR_GRP_CURVE_A:
+ case OAKLEY_ATTR_GRP_CURVE_B:
+ case OAKLEY_ATTR_SA_LD:
+ case OAKLEY_ATTR_GRP_ORDER:
+ if (flag) { /*TV*/
+ len = 2;
+ p = (u_char *)&d->lorv;
+ } else { /*TLV*/
+ len = ntohs(d->lorv);
+ p = (u_char *)(d + 1);
+ }
+ val = vmalloc(len);
+ if (!val)
+ return -1;
+ memcpy(val->v, p, len);
+ break;
+
+ default:
+ break;
+ }
+
+ switch (type) {
+ case OAKLEY_ATTR_ENC_ALG:
+ sa->enctype = (u_int16_t)ntohs(d->lorv);
+ break;
+
+ case OAKLEY_ATTR_HASH_ALG:
+ sa->hashtype = (u_int16_t)ntohs(d->lorv);
+ break;
+
+ case OAKLEY_ATTR_AUTH_METHOD:
+ sa->authmethod = ntohs(d->lorv);
+#ifdef HAVE_GSSAPI
+ if (sa->authmethod == OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB_REAL &&
+ (vendorid_mask & VENDORID_GSSAPI_MASK))
+ sa->authmethod = OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB;
+#endif
+ break;
+
+ case OAKLEY_ATTR_GRP_DESC:
+ sa->dh_group = (u_int16_t)ntohs(d->lorv);
+ break;
+
+ case OAKLEY_ATTR_GRP_TYPE:
+ {
+ int type = (int)ntohs(d->lorv);
+ if (type == OAKLEY_ATTR_GRP_TYPE_MODP)
+ sa->dhgrp->type = type;
+ else
+ return -1;
+ break;
+ }
+ case OAKLEY_ATTR_GRP_PI:
+ sa->dhgrp->prime = val;
+ break;
+
+ case OAKLEY_ATTR_GRP_GEN_ONE:
+ vfree(val);
+ if (!flag)
+ sa->dhgrp->gen1 = ntohs(d->lorv);
+ else {
+ int len = ntohs(d->lorv);
+ sa->dhgrp->gen1 = 0;
+ if (len > 4)
+ return -1;
+ memcpy(&sa->dhgrp->gen1, d + 1, len);
+ sa->dhgrp->gen1 = ntohl(sa->dhgrp->gen1);
+ }
+ break;
+
+ case OAKLEY_ATTR_GRP_GEN_TWO:
+ vfree(val);
+ if (!flag)
+ sa->dhgrp->gen2 = ntohs(d->lorv);
+ else {
+ int len = ntohs(d->lorv);
+ sa->dhgrp->gen2 = 0;
+ if (len > 4)
+ return -1;
+ memcpy(&sa->dhgrp->gen2, d + 1, len);
+ sa->dhgrp->gen2 = ntohl(sa->dhgrp->gen2);
+ }
+ break;
+
+ case OAKLEY_ATTR_GRP_CURVE_A:
+ sa->dhgrp->curve_a = val;
+ break;
+
+ case OAKLEY_ATTR_GRP_CURVE_B:
+ sa->dhgrp->curve_b = val;
+ break;
+
+ case OAKLEY_ATTR_SA_LD_TYPE:
+ {
+ int type = (int)ntohs(d->lorv);
+ switch (type) {
+ case OAKLEY_ATTR_SA_LD_TYPE_SEC:
+ case OAKLEY_ATTR_SA_LD_TYPE_KB:
+ life_t = type;
+ break;
+ default:
+ life_t = OAKLEY_ATTR_SA_LD_TYPE_DEFAULT;
+ break;
+ }
+ break;
+ }
+ case OAKLEY_ATTR_SA_LD:
+ if (!prev
+ || (ntohs(prev->type) & ~ISAKMP_GEN_MASK) !=
+ OAKLEY_ATTR_SA_LD_TYPE) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "life duration must follow ltype\n");
+ break;
+ }
+
+ switch (life_t) {
+ case IPSECDOI_ATTR_SA_LD_TYPE_SEC:
+ sa->lifetime = ipsecdoi_set_ld(val);
+ vfree(val);
+ if (sa->lifetime == 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid life duration.\n");
+ goto err;
+ }
+ break;
+ case IPSECDOI_ATTR_SA_LD_TYPE_KB:
+ sa->lifebyte = ipsecdoi_set_ld(val);
+ vfree(val);
+ if (sa->lifebyte == 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid life duration.\n");
+ goto err;
+ }
+ break;
+ default:
+ vfree(val);
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid life type: %d\n", life_t);
+ goto err;
+ }
+ break;
+
+ case OAKLEY_ATTR_KEY_LEN:
+ {
+ int len = ntohs(d->lorv);
+ if (len % 8 != 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "keylen %d: not multiple of 8\n",
+ len);
+ goto err;
+ }
+ sa->encklen = (u_int16_t)len;
+ keylen++;
+ break;
+ }
+ case OAKLEY_ATTR_PRF:
+ case OAKLEY_ATTR_FIELD_SIZE:
+ /* unsupported */
+ break;
+
+ case OAKLEY_ATTR_GRP_ORDER:
+ sa->dhgrp->order = val;
+ break;
+#ifdef HAVE_GSSAPI
+ case OAKLEY_ATTR_GSS_ID:
+ {
+ int error = -1;
+ iconv_t cd = (iconv_t) -1;
+ size_t srcleft, dstleft, rv;
+ __iconv_const char *src;
+ char *dst;
+ int len = ntohs(d->lorv);
+
+ /*
+ * Older verions of racoon just placed the
+ * ISO-Latin-1 string on the wire directly.
+ * Check to see if we are configured to be
+ * compatible with this behavior.
+ */
+ if (lcconf->gss_id_enc == LC_GSSENC_LATIN1) {
+ if ((sa->gssid = vmalloc(len)) == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate memory\n");
+ goto out;
+ }
+ memcpy(sa->gssid->v, d + 1, len);
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "received old-style gss "
+ "id '%.*s' (len %zu)\n",
+ (int)sa->gssid->l, sa->gssid->v,
+ sa->gssid->l);
+ error = 0;
+ goto out;
+ }
+
+ /*
+ * For Windows 2000 compatibility, we expect
+ * the GSS ID attribute on the wire to be
+ * encoded in UTF-16LE. Internally, we work
+ * in ISO-Latin-1. Therefore, we should need
+ * 1/2 the specified length, which should always
+ * be a multiple of 2 octets.
+ */
+ cd = iconv_open("latin1", "utf-16le");
+ if (cd == (iconv_t) -1) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "unable to initialize utf-16le -> latin1 "
+ "conversion descriptor: %s\n",
+ strerror(errno));
+ goto out;
+ }
+
+ if ((sa->gssid = vmalloc(len / 2)) == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate memory\n");
+ goto out;
+ }
+
+ src = (__iconv_const char *)(d + 1);
+ srcleft = len;
+
+ dst = sa->gssid->v;
+ dstleft = len / 2;
+
+ rv = iconv(cd, (__iconv_const char **)&src, &srcleft,
+ &dst, &dstleft);
+ if (rv != 0) {
+ if (rv == -1) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "unable to convert GSS ID from "
+ "utf-16le -> latin1: %s\n",
+ strerror(errno));
+ } else {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "%zd character%s in GSS ID cannot "
+ "be represented in latin1\n",
+ rv, rv == 1 ? "" : "s");
+ }
+ goto out;
+ }
+
+ /* XXX dstleft should always be 0; assert it? */
+ sa->gssid->l = (len / 2) - dstleft;
+
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "received gss id '%.*s' (len %zu)\n",
+ (int)sa->gssid->l, sa->gssid->v, sa->gssid->l);
+
+ error = 0;
+out:
+ if (cd != (iconv_t)-1)
+ (void)iconv_close(cd);
+
+ if ((error != 0) && (sa->gssid != NULL)) {
+ vfree(sa->gssid);
+ sa->gssid = NULL;
+ }
+ break;
+ }
+#endif /* HAVE_GSSAPI */
+
+ default:
+ break;
+ }
+
+ prev = d;
+ if (flag) {
+ tlen -= sizeof(*d);
+ d = (struct isakmp_data *)((char *)d + sizeof(*d));
+ } else {
+ tlen -= (sizeof(*d) + ntohs(d->lorv));
+ d = (struct isakmp_data *)((char *)d + sizeof(*d) + ntohs(d->lorv));
+ }
+ }
+
+ /* key length must not be specified on some algorithms */
+ if (keylen) {
+ if (sa->enctype == OAKLEY_ATTR_ENC_ALG_DES
+#ifdef HAVE_OPENSSL_IDEA_H
+ || sa->enctype == OAKLEY_ATTR_ENC_ALG_IDEA
+#endif
+ || sa->enctype == OAKLEY_ATTR_ENC_ALG_3DES) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "keylen must not be specified "
+ "for encryption algorithm %d\n",
+ sa->enctype);
+ return -1;
+ }
+ }
+
+ return 0;
+err:
+ return error;
+}
+
+/*%%%*/
+/*
+ * check phase 2 SA payload and select single proposal.
+ * make new SA payload to be replyed not including general header.
+ * This function is called by responder only.
+ * OUT:
+ * 0: succeed.
+ * -1: error occured.
+ */
+int
+ipsecdoi_selectph2proposal(iph2)
+ struct ph2handle *iph2;
+{
+ struct prop_pair **pair;
+ struct prop_pair *ret;
+ u_int32_t doitype, sittype;
+
+ /* get proposal pair */
+ pair = get_proppair_and_doi_sit(iph2->sa, IPSECDOI_TYPE_PH2,
+ &doitype, &sittype);
+ if (pair == NULL)
+ return -1;
+
+ /* check and select a proposal. */
+ ret = get_ph2approval(iph2, pair);
+ free_proppair(pair);
+ if (ret == NULL)
+ return -1;
+
+ /* make a SA to be replayed. */
+ /* SPI must be updated later. */
+ iph2->sa_ret = get_sabyproppair(doitype, sittype, ret);
+ free_proppair0(ret);
+ if (iph2->sa_ret == NULL)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * check phase 2 SA payload returned from responder.
+ * This function is called by initiator only.
+ * OUT:
+ * 0: valid.
+ * -1: invalid.
+ */
+int
+ipsecdoi_checkph2proposal(iph2)
+ struct ph2handle *iph2;
+{
+ struct prop_pair **rpair = NULL, **spair = NULL;
+ struct prop_pair *p;
+ int i, n, num;
+ int error = -1;
+ vchar_t *sa_ret = NULL;
+ u_int32_t doitype, sittype;
+
+ /* get proposal pair of SA sent. */
+ spair = get_proppair_and_doi_sit(iph2->sa, IPSECDOI_TYPE_PH2,
+ &doitype, &sittype);
+ if (spair == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get prop pair.\n");
+ goto end;
+ }
+
+ /* XXX should check the number of transform */
+
+ /* get proposal pair of SA replayed */
+ rpair = get_proppair(iph2->sa_ret, IPSECDOI_TYPE_PH2);
+ if (rpair == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get prop pair.\n");
+ goto end;
+ }
+
+ /* check proposal is only one ? */
+ n = 0;
+ num = 0;
+ for (i = 0; i < MAXPROPPAIRLEN; i++) {
+ if (rpair[i]) {
+ n = i;
+ num++;
+ }
+ }
+ if (num == 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "no proposal received.\n");
+ goto end;
+ }
+ if (num != 1) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "some proposals received.\n");
+ goto end;
+ }
+
+ if (spair[n] == NULL) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "invalid proposal number:%d received.\n", i);
+ }
+
+
+ if (rpair[n]->tnext != NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "multi transforms replyed.\n");
+ goto end;
+ }
+
+ if (cmp_aproppair_i(rpair[n], spair[n])) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "proposal mismathed.\n");
+ goto end;
+ }
+
+ /*
+ * check and select a proposal.
+ * ensure that there is no modification of the proposal by
+ * cmp_aproppair_i()
+ */
+ p = get_ph2approval(iph2, rpair);
+ if (p == NULL)
+ goto end;
+
+ /* make a SA to be replayed. */
+ sa_ret = iph2->sa_ret;
+ iph2->sa_ret = get_sabyproppair(doitype, sittype, p);
+ free_proppair0(p);
+ if (iph2->sa_ret == NULL)
+ goto end;
+
+ error = 0;
+
+end:
+ if (rpair)
+ free_proppair(rpair);
+ if (spair)
+ free_proppair(spair);
+ if (sa_ret)
+ vfree(sa_ret);
+
+ return error;
+}
+
+/*
+ * compare two prop_pair which is assumed to have same proposal number.
+ * the case of bundle or single SA, NOT multi transforms.
+ * a: a proposal that is multi protocols and single transform, usually replyed.
+ * b: a proposal that is multi protocols and multi transform, usually sent.
+ * NOTE: this function is for initiator.
+ * OUT
+ * 0: equal
+ * 1: not equal
+ * XXX cannot understand the comment!
+ */
+static int
+cmp_aproppair_i(a, b)
+ struct prop_pair *a, *b;
+{
+ struct prop_pair *p, *q, *r;
+ int len;
+
+ for (p = a, q = b; p && q; p = p->next, q = q->next) {
+ for (r = q; r; r = r->tnext) {
+ /* compare trns */
+ if (p->trns->t_no == r->trns->t_no)
+ break;
+ }
+ if (!r) {
+ /* no suitable transform found */
+ plog(LLV_ERROR, LOCATION, NULL,
+ "no suitable transform found.\n");
+ return -1;
+ }
+
+ /* compare prop */
+ if (p->prop->p_no != r->prop->p_no) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "proposal #%d mismatched, "
+ "expected #%d.\n",
+ r->prop->p_no, p->prop->p_no);
+ /*FALLTHROUGH*/
+ }
+
+ if (p->prop->proto_id != r->prop->proto_id) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "proto_id mismathed: my:%d peer:%d\n",
+ r->prop->proto_id, p->prop->proto_id);
+ return -1;
+ }
+
+ if (p->prop->spi_size != r->prop->spi_size) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid spi size: %d.\n",
+ p->prop->spi_size);
+ return -1;
+ }
+
+ /* check #of transforms */
+ if (p->prop->num_t != 1) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "#of transform is %d, "
+ "but expected 1.\n", p->prop->num_t);
+ /*FALLTHROUGH*/
+ }
+
+ if (p->trns->t_id != r->trns->t_id) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "transform number has been modified.\n");
+ /*FALLTHROUGH*/
+ }
+ if (p->trns->reserved != r->trns->reserved) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "reserved field should be zero.\n");
+ /*FALLTHROUGH*/
+ }
+
+ /* compare attribute */
+ len = ntohs(r->trns->h.len) - sizeof(*p->trns);
+ if (memcmp(p->trns + 1, r->trns + 1, len) != 0) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "attribute has been modified.\n");
+ /*FALLTHROUGH*/
+ }
+ }
+ if ((p && !q) || (!p && q)) {
+ /* # of protocols mismatched */
+ plog(LLV_ERROR, LOCATION, NULL,
+ "#of protocols mismatched.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * acceptable check for policy configuration.
+ * return a new SA payload to be reply to peer.
+ */
+static struct prop_pair *
+get_ph2approval(iph2, pair)
+ struct ph2handle *iph2;
+ struct prop_pair **pair;
+{
+ struct prop_pair *ret;
+ int i;
+
+ iph2->approval = NULL;
+
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "begin compare proposals.\n");
+
+ for (i = 0; i < MAXPROPPAIRLEN; i++) {
+ if (pair[i] == NULL)
+ continue;
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "pair[%d]: %p\n", i, pair[i]);
+ print_proppair(LLV_DEBUG, pair[i]);;
+
+ /* compare proposal and select one */
+ ret = get_ph2approvalx(iph2, pair[i]);
+ if (ret != NULL) {
+ /* found */
+ return ret;
+ }
+ }
+
+ plog(LLV_ERROR, LOCATION, NULL, "no suitable policy found.\n");
+
+ return NULL;
+}
+
+/*
+ * compare my proposal and peers just one proposal.
+ * set a approval.
+ */
+static struct prop_pair *
+get_ph2approvalx(iph2, pp)
+ struct ph2handle *iph2;
+ struct prop_pair *pp;
+{
+ struct prop_pair *ret = NULL;
+ struct saprop *pr0, *pr = NULL;
+ struct saprop *q1, *q2;
+
+ pr0 = aproppair2saprop(pp);
+ if (pr0 == NULL)
+ return NULL;
+
+ for (q1 = pr0; q1; q1 = q1->next) {
+ for (q2 = iph2->proposal; q2; q2 = q2->next) {
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "peer's single bundle:\n");
+ printsaprop0(LLV_DEBUG, q1);
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "my single bundle:\n");
+ printsaprop0(LLV_DEBUG, q2);
+
+ pr = cmpsaprop_alloc(iph2->ph1, q1, q2, iph2->side);
+ if (pr != NULL)
+ goto found;
+
+ plog(LLV_ERROR, LOCATION, NULL,
+ "not matched\n");
+ }
+ }
+ /* no proposal matching */
+err:
+ flushsaprop(pr0);
+ return NULL;
+
+found:
+ flushsaprop(pr0);
+ plog(LLV_DEBUG, LOCATION, NULL, "matched\n");
+ iph2->approval = pr;
+
+ {
+ struct saproto *sp;
+ struct prop_pair *p, *x;
+ struct prop_pair *n = NULL;
+
+ ret = NULL;
+
+ for (p = pp; p; p = p->next) {
+ /*
+ * find a proposal with matching proto_id.
+ * we have analyzed validity already, in cmpsaprop_alloc().
+ */
+ for (sp = pr->head; sp; sp = sp->next) {
+ if (sp->proto_id == p->prop->proto_id)
+ break;
+ }
+ if (!sp)
+ goto err;
+ if (sp->head->next)
+ goto err; /* XXX */
+
+ for (x = p; x; x = x->tnext)
+ if (sp->head->trns_no == x->trns->t_no)
+ break;
+ if (!x)
+ goto err; /* XXX */
+
+ n = racoon_calloc(1, sizeof(struct prop_pair));
+ if (n == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get buffer.\n");
+ goto err;
+ }
+
+ n->prop = x->prop;
+ n->trns = x->trns;
+
+ /* need to preserve the order */
+ for (x = ret; x && x->next; x = x->next)
+ ;
+ if (x && x->prop == n->prop) {
+ for (/*nothing*/; x && x->tnext; x = x->tnext)
+ ;
+ x->tnext = n;
+ } else {
+ if (x)
+ x->next = n;
+ else {
+ ret = n;
+ }
+ }
+
+ /* #of transforms should be updated ? */
+ }
+ }
+
+ return ret;
+}
+
+void
+free_proppair(pair)
+ struct prop_pair **pair;
+{
+ int i;
+
+ for (i = 0; i < MAXPROPPAIRLEN; i++) {
+ free_proppair0(pair[i]);
+ pair[i] = NULL;
+ }
+ racoon_free(pair);
+}
+
+static void
+free_proppair0(pair)
+ struct prop_pair *pair;
+{
+ struct prop_pair *p, *q, *r, *s;
+
+ p = pair;
+ while (p) {
+ q = p->next;
+ r = p;
+ while (r) {
+ s = r->tnext;
+ racoon_free(r);
+ r = s;
+ }
+ p = q;
+ }
+}
+
+/*
+ * get proposal pairs from SA payload.
+ * tiny check for proposal payload.
+ */
+static struct prop_pair **
+get_proppair_and_doi_sit(sa, mode, doitype, sittype)
+ vchar_t *sa;
+ int mode;
+ u_int32_t *doitype, *sittype;
+{
+ struct prop_pair **pair = NULL;
+ int num_p = 0; /* number of proposal for use */
+ int tlen;
+ caddr_t bp;
+ int i;
+ struct ipsecdoi_sa_b *sab = (struct ipsecdoi_sa_b *)sa->v;
+
+ plog(LLV_DEBUG, LOCATION, NULL, "total SA len=%zu\n", sa->l);
+ plogdump(LLV_DEBUG, sa->v, sa->l);
+
+ /* check SA payload size */
+ if (sa->l < sizeof(*sab)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Invalid SA length = %zu.\n", sa->l);
+ goto bad;
+ }
+
+ /* check DOI */
+ if (check_doi(ntohl(sab->doi)) < 0)
+ goto bad;
+ if (doitype != NULL)
+ *doitype = ntohl(sab->doi);
+
+ /* check SITUATION */
+ if (check_situation(ntohl(sab->sit)) < 0)
+ goto bad;
+ if (sittype != NULL)
+ *sittype = ntohl(sab->sit);
+
+ pair = racoon_calloc(1, MAXPROPPAIRLEN * sizeof(*pair));
+ if (pair == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get buffer.\n");
+ goto bad;
+ }
+
+ bp = (caddr_t)(sab + 1);
+ tlen = sa->l - sizeof(*sab);
+
+ {
+ struct isakmp_pl_p *prop;
+ int proplen;
+ vchar_t *pbuf = NULL;
+ struct isakmp_parse_t *pa;
+
+ pbuf = isakmp_parsewoh(ISAKMP_NPTYPE_P, (struct isakmp_gen *)bp, tlen);
+ if (pbuf == NULL)
+ goto bad;
+
+ for (pa = (struct isakmp_parse_t *)pbuf->v;
+ pa->type != ISAKMP_NPTYPE_NONE;
+ pa++) {
+ /* check the value of next payload */
+ if (pa->type != ISAKMP_NPTYPE_P) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Invalid payload type=%u\n", pa->type);
+ vfree(pbuf);
+ goto bad;
+ }
+
+ prop = (struct isakmp_pl_p *)pa->ptr;
+ proplen = pa->len;
+
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "proposal #%u len=%d\n", prop->p_no, proplen);
+
+ if (proplen == 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid proposal with length %d\n", proplen);
+ vfree(pbuf);
+ goto bad;
+ }
+
+ /* check Protocol ID */
+ if (!check_protocol[mode]) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "unsupported mode %d\n", mode);
+ continue;
+ }
+
+ if (check_protocol[mode](prop->proto_id) < 0)
+ continue;
+
+ /* check SPI length when IKE. */
+ if (check_spi_size(prop->proto_id, prop->spi_size) < 0)
+ continue;
+
+ /* get transform */
+ if (get_transform(prop, pair, &num_p) < 0) {
+ vfree(pbuf);
+ goto bad;
+ }
+ }
+ vfree(pbuf);
+ pbuf = NULL;
+ }
+
+ {
+ int notrans, nprop;
+ struct prop_pair *p, *q;
+
+ /* check for proposals with no transforms */
+ for (i = 0; i < MAXPROPPAIRLEN; i++) {
+ if (!pair[i])
+ continue;
+
+ plog(LLV_DEBUG, LOCATION, NULL, "pair %d:\n", i);
+ print_proppair(LLV_DEBUG, pair[i]);
+
+ notrans = nprop = 0;
+ for (p = pair[i]; p; p = p->next) {
+ if (p->trns == NULL) {
+ notrans++;
+ break;
+ }
+ for (q = p; q; q = q->tnext)
+ nprop++;
+ }
+
+#if 0
+ /*
+ * XXX at this moment, we cannot accept proposal group
+ * with multiple proposals. this should be fixed.
+ */
+ if (pair[i]->next) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "proposal #%u ignored "
+ "(multiple proposal not supported)\n",
+ pair[i]->prop->p_no);
+ notrans++;
+ }
+#endif
+
+ if (notrans) {
+ for (p = pair[i]; p; p = q) {
+ q = p->next;
+ racoon_free(p);
+ }
+ pair[i] = NULL;
+ num_p--;
+ } else {
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "proposal #%u: %d transform\n",
+ pair[i]->prop->p_no, nprop);
+ }
+ }
+ }
+
+ /* bark if no proposal is found. */
+ if (num_p <= 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "no Proposal found.\n");
+ goto bad;
+ }
+
+ return pair;
+bad:
+ if (pair != NULL)
+ racoon_free(pair);
+ return NULL;
+}
+
+struct prop_pair **
+get_proppair(sa, mode)
+ vchar_t *sa;
+ int mode;
+{
+ return get_proppair_and_doi_sit(sa, mode, NULL, NULL);
+}
+
+
+/*
+ * check transform payload.
+ * OUT:
+ * positive: return the pointer to the payload of valid transform.
+ * 0 : No valid transform found.
+ */
+static int
+get_transform(prop, pair, num_p)
+ struct isakmp_pl_p *prop;
+ struct prop_pair **pair;
+ int *num_p;
+{
+ int tlen; /* total length of all transform in a proposal */
+ caddr_t bp;
+ struct isakmp_pl_t *trns;
+ int trnslen;
+ vchar_t *pbuf = NULL;
+ struct isakmp_parse_t *pa;
+ struct prop_pair *p = NULL, *q;
+ int num_t;
+
+ bp = (caddr_t)prop + sizeof(struct isakmp_pl_p) + prop->spi_size;
+ tlen = ntohs(prop->h.len)
+ - (sizeof(struct isakmp_pl_p) + prop->spi_size);
+ pbuf = isakmp_parsewoh(ISAKMP_NPTYPE_T, (struct isakmp_gen *)bp, tlen);
+ if (pbuf == NULL)
+ return -1;
+
+ /* check and get transform for use */
+ num_t = 0;
+ for (pa = (struct isakmp_parse_t *)pbuf->v;
+ pa->type != ISAKMP_NPTYPE_NONE;
+ pa++) {
+
+ num_t++;
+
+ /* check the value of next payload */
+ if (pa->type != ISAKMP_NPTYPE_T) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Invalid payload type=%u\n", pa->type);
+ break;
+ }
+
+ trns = (struct isakmp_pl_t *)pa->ptr;
+ trnslen = pa->len;
+
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "transform #%u len=%u\n", trns->t_no, trnslen);
+
+ /* check transform ID */
+ if (prop->proto_id >= ARRAYLEN(check_transform)) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "unsupported proto_id %u\n",
+ prop->proto_id);
+ continue;
+ }
+ if (prop->proto_id >= ARRAYLEN(check_attributes)) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "unsupported proto_id %u\n",
+ prop->proto_id);
+ continue;
+ }
+
+ if (!check_transform[prop->proto_id]
+ || !check_attributes[prop->proto_id]) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "unsupported proto_id %u\n",
+ prop->proto_id);
+ continue;
+ }
+ if (check_transform[prop->proto_id](trns->t_id) < 0)
+ continue;
+
+ /* check data attributes */
+ if (check_attributes[prop->proto_id](trns) != 0)
+ continue;
+
+ p = racoon_calloc(1, sizeof(*p));
+ if (p == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get buffer.\n");
+ vfree(pbuf);
+ return -1;
+ }
+ p->prop = prop;
+ p->trns = trns;
+
+ /* need to preserve the order */
+ for (q = pair[prop->p_no]; q && q->next; q = q->next)
+ ;
+ if (q && q->prop == p->prop) {
+ for (/*nothing*/; q && q->tnext; q = q->tnext)
+ ;
+ q->tnext = p;
+ } else {
+ if (q)
+ q->next = p;
+ else {
+ pair[prop->p_no] = p;
+ (*num_p)++;
+ }
+ }
+ }
+
+ vfree(pbuf);
+
+ return 0;
+}
+
+/*
+ * make a new SA payload from prop_pair.
+ * NOTE: this function make spi value clear.
+ */
+vchar_t *
+get_sabyproppair(doitype, sittype, pair)
+ u_int32_t doitype, sittype;
+ struct prop_pair *pair;
+{
+ vchar_t *newsa;
+ int newtlen;
+ u_int8_t *np_p = NULL;
+ struct prop_pair *p;
+ int prophlen, trnslen;
+ caddr_t bp;
+
+ newtlen = sizeof(struct ipsecdoi_sa_b);
+ for (p = pair; p; p = p->next) {
+ newtlen += sizeof(struct isakmp_pl_p);
+ newtlen += p->prop->spi_size;
+ newtlen += ntohs(p->trns->h.len);
+ }
+
+ newsa = vmalloc(newtlen);
+ if (newsa == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL, "failed to get newsa.\n");
+ return NULL;
+ }
+ bp = newsa->v;
+
+ ((struct isakmp_gen *)bp)->len = htons(newtlen);
+
+ /* update some of values in SA header */
+ ((struct ipsecdoi_sa_b *)bp)->doi = htonl(doitype);
+ ((struct ipsecdoi_sa_b *)bp)->sit = htonl(sittype);
+ bp += sizeof(struct ipsecdoi_sa_b);
+
+ /* create proposal payloads */
+ for (p = pair; p; p = p->next) {
+ prophlen = sizeof(struct isakmp_pl_p)
+ + p->prop->spi_size;
+ trnslen = ntohs(p->trns->h.len);
+
+ if (np_p)
+ *np_p = ISAKMP_NPTYPE_P;
+
+ /* create proposal */
+
+ memcpy(bp, p->prop, prophlen);
+ ((struct isakmp_pl_p *)bp)->h.np = ISAKMP_NPTYPE_NONE;
+ ((struct isakmp_pl_p *)bp)->h.len = htons(prophlen + trnslen);
+ ((struct isakmp_pl_p *)bp)->num_t = 1;
+ np_p = &((struct isakmp_pl_p *)bp)->h.np;
+ memset(bp + sizeof(struct isakmp_pl_p), 0, p->prop->spi_size);
+ bp += prophlen;
+
+ /* create transform */
+ memcpy(bp, p->trns, trnslen);
+ ((struct isakmp_pl_t *)bp)->h.np = ISAKMP_NPTYPE_NONE;
+ ((struct isakmp_pl_t *)bp)->h.len = htons(trnslen);
+ bp += trnslen;
+ }
+
+ return newsa;
+}
+
+/*
+ * update responder's spi
+ */
+int
+ipsecdoi_updatespi(iph2)
+ struct ph2handle *iph2;
+{
+ struct prop_pair **pair, *p;
+ struct saprop *pp;
+ struct saproto *pr;
+ int i;
+ int error = -1;
+ u_int8_t *spi;
+
+ pair = get_proppair(iph2->sa_ret, IPSECDOI_TYPE_PH2);
+ if (pair == NULL)
+ return -1;
+ for (i = 0; i < MAXPROPPAIRLEN; i++) {
+ if (pair[i])
+ break;
+ }
+ if (i == MAXPROPPAIRLEN || pair[i]->tnext) {
+ /* multiple transform must be filtered by selectph2proposal.*/
+ goto end;
+ }
+
+ pp = iph2->approval;
+
+ /* create proposal payloads */
+ for (p = pair[i]; p; p = p->next) {
+ /*
+ * find a proposal/transform with matching proto_id/t_id.
+ * we have analyzed validity already, in cmpsaprop_alloc().
+ */
+ for (pr = pp->head; pr; pr = pr->next) {
+ if (p->prop->proto_id == pr->proto_id &&
+ p->trns->t_id == pr->head->trns_id) {
+ break;
+ }
+ }
+ if (!pr)
+ goto end;
+
+ /*
+ * XXX SPI bits are left-filled, for use with IPComp.
+ * we should be switching to variable-length spi field...
+ */
+ spi = (u_int8_t *)&pr->spi;
+ spi += sizeof(pr->spi);
+ spi -= pr->spisize;
+ memcpy((caddr_t)p->prop + sizeof(*p->prop), spi, pr->spisize);
+ }
+
+ error = 0;
+end:
+ free_proppair(pair);
+ return error;
+}
+
+/*
+ * make a new SA payload from prop_pair.
+ */
+vchar_t *
+get_sabysaprop(pp0, sa0)
+ struct saprop *pp0;
+ vchar_t *sa0;
+{
+ struct prop_pair **pair = NULL;
+ vchar_t *newsa = NULL;
+ int newtlen;
+ u_int8_t *np_p = NULL;
+ struct prop_pair *p = NULL;
+ struct saprop *pp;
+ struct saproto *pr;
+ struct satrns *tr;
+ int prophlen, trnslen;
+ caddr_t bp;
+ int error = -1;
+
+ /* get proposal pair */
+ pair = get_proppair(sa0, IPSECDOI_TYPE_PH2);
+ if (pair == NULL)
+ goto out;
+
+ newtlen = sizeof(struct ipsecdoi_sa_b);
+ for (pp = pp0; pp; pp = pp->next) {
+
+ if (pair[pp->prop_no] == NULL)
+ goto out;
+
+ for (pr = pp->head; pr; pr = pr->next) {
+ newtlen += (sizeof(struct isakmp_pl_p)
+ + pr->spisize);
+
+ for (tr = pr->head; tr; tr = tr->next) {
+ for (p = pair[pp->prop_no]; p; p = p->tnext) {
+ if (tr->trns_no == p->trns->t_no)
+ break;
+ }
+ if (p == NULL)
+ goto out;
+
+ newtlen += ntohs(p->trns->h.len);
+ }
+ }
+ }
+
+ newsa = vmalloc(newtlen);
+ if (newsa == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL, "failed to get newsa.\n");
+ goto out;
+ }
+ bp = newsa->v;
+
+ /* some of values of SA must be updated in the out of this function */
+ ((struct isakmp_gen *)bp)->len = htons(newtlen);
+ bp += sizeof(struct ipsecdoi_sa_b);
+
+ /* create proposal payloads */
+ for (pp = pp0; pp; pp = pp->next) {
+
+ for (pr = pp->head; pr; pr = pr->next) {
+ prophlen = sizeof(struct isakmp_pl_p)
+ + p->prop->spi_size;
+
+ for (tr = pr->head; tr; tr = tr->next) {
+ for (p = pair[pp->prop_no]; p; p = p->tnext) {
+ if (tr->trns_no == p->trns->t_no)
+ break;
+ }
+ if (p == NULL)
+ goto out;
+
+ trnslen = ntohs(p->trns->h.len);
+
+ if (np_p)
+ *np_p = ISAKMP_NPTYPE_P;
+
+ /* create proposal */
+
+ memcpy(bp, p->prop, prophlen);
+ ((struct isakmp_pl_p *)bp)->h.np = ISAKMP_NPTYPE_NONE;
+ ((struct isakmp_pl_p *)bp)->h.len = htons(prophlen + trnslen);
+ ((struct isakmp_pl_p *)bp)->num_t = 1;
+ np_p = &((struct isakmp_pl_p *)bp)->h.np;
+ bp += prophlen;
+
+ /* create transform */
+ memcpy(bp, p->trns, trnslen);
+ ((struct isakmp_pl_t *)bp)->h.np = ISAKMP_NPTYPE_NONE;
+ ((struct isakmp_pl_t *)bp)->h.len = htons(trnslen);
+ bp += trnslen;
+ }
+ }
+ }
+
+ error = 0;
+out:
+ if (pair != NULL)
+ racoon_free(pair);
+
+ if (error != 0) {
+ if (newsa != NULL) {
+ vfree(newsa);
+ newsa = NULL;
+ }
+ }
+
+ return newsa;
+}
+
+/*
+ * If some error happens then return 0. Although 0 means that lifetime is zero,
+ * such a value should not be accepted.
+ * Also 0 of lifebyte should not be included in a packet although 0 means not
+ * to care of it.
+ */
+static u_int32_t
+ipsecdoi_set_ld(buf)
+ vchar_t *buf;
+{
+ u_int32_t ld;
+
+ if (buf == 0)
+ return 0;
+
+ switch (buf->l) {
+ case 2:
+ ld = ntohs(*(u_int16_t *)buf->v);
+ break;
+ case 4:
+ ld = ntohl(*(u_int32_t *)buf->v);
+ break;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "length %zu of life duration "
+ "isn't supported.\n", buf->l);
+ return 0;
+ }
+
+ return ld;
+}
+
+/*
+ * parse responder-lifetime attributes from payload
+ */
+int
+ipsecdoi_parse_responder_lifetime(notify, lifetime_sec, lifetime_kb)
+ struct isakmp_pl_n *notify;
+ u_int32_t *lifetime_sec;
+ u_int32_t *lifetime_kb;
+{
+ struct isakmp_data *d;
+ int flag, type, tlen, ld_type = -1;
+ u_int16_t lorv;
+ u_int32_t value;
+
+ tlen = ntohs(notify->h.len) - sizeof(*notify) - notify->spi_size;
+ d = (struct isakmp_data *)((char *)(notify + 1) +
+ notify->spi_size);
+
+ while (tlen >= sizeof(struct isakmp_data)) {
+ type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
+ flag = ntohs(d->type) & ISAKMP_GEN_MASK;
+ lorv = ntohs(d->lorv);
+
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "type=%s, flag=0x%04x, lorv=%s\n",
+ s_ipsecdoi_attr(type), flag,
+ s_ipsecdoi_attr_v(type, lorv));
+
+ switch (type) {
+ case IPSECDOI_ATTR_SA_LD_TYPE:
+ if (! flag) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "must be TV when LD_TYPE.\n");
+ return -1;
+ }
+ ld_type = lorv;
+ break;
+ case IPSECDOI_ATTR_SA_LD:
+ if (flag)
+ value = lorv;
+ else if (lorv == 2)
+ value = ntohs(*(u_int16_t *)(d + 1));
+ else if (lorv == 4)
+ value = ntohl(*(u_int32_t *)(d + 1));
+ else {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "payload length %d for lifetime "
+ "data length is unsupported.\n", lorv);
+ return -1;
+ }
+
+ switch (ld_type) {
+ case IPSECDOI_ATTR_SA_LD_TYPE_SEC:
+ if (lifetime_sec != NULL)
+ *lifetime_sec = value;
+ plog(LLV_INFO, LOCATION, NULL,
+ "received RESPONDER-LIFETIME: %d "
+ "seconds\n", value);
+ break;
+ case IPSECDOI_ATTR_SA_LD_TYPE_KB:
+ if (lifetime_kb != NULL)
+ *lifetime_kb = value;
+ plog(LLV_INFO, LOCATION, NULL,
+ "received RESPONDER-LIFETIME: %d "
+ "kbytes\n", value);
+ break;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "lifetime data received without "
+ "lifetime data type.\n");
+ return -1;
+ }
+ break;
+ }
+
+ if (flag) {
+ tlen -= sizeof(*d);
+ d = (struct isakmp_data *)((char *)d
+ + sizeof(*d));
+ } else {
+ tlen -= (sizeof(*d) + lorv);
+ d = (struct isakmp_data *)((char *)d
+ + sizeof(*d) + lorv);
+ }
+ }
+
+ return 0;
+}
+
+
+/*%%%*/
+/*
+ * check DOI
+ */
+static int
+check_doi(doi)
+ u_int32_t doi;
+{
+ switch (doi) {
+ case IPSEC_DOI:
+ return 0;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid value of DOI 0x%08x.\n", doi);
+ return -1;
+ }
+ /* NOT REACHED */
+}
+
+/*
+ * check situation
+ */
+static int
+check_situation(sit)
+ u_int32_t sit;
+{
+ switch (sit) {
+ case IPSECDOI_SIT_IDENTITY_ONLY:
+ return 0;
+
+ case IPSECDOI_SIT_SECRECY:
+ case IPSECDOI_SIT_INTEGRITY:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "situation 0x%08x unsupported yet.\n", sit);
+ return -1;
+
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid situation 0x%08x.\n", sit);
+ return -1;
+ }
+ /* NOT REACHED */
+}
+
+/*
+ * check protocol id in main mode
+ */
+static int
+check_prot_main(proto_id)
+ int proto_id;
+{
+ switch (proto_id) {
+ case IPSECDOI_PROTO_ISAKMP:
+ return 0;
+
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Illegal protocol id=%u.\n", proto_id);
+ return -1;
+ }
+ /* NOT REACHED */
+}
+
+/*
+ * check protocol id in quick mode
+ */
+static int
+check_prot_quick(proto_id)
+ int proto_id;
+{
+ switch (proto_id) {
+ case IPSECDOI_PROTO_IPSEC_AH:
+ case IPSECDOI_PROTO_IPSEC_ESP:
+ return 0;
+
+ case IPSECDOI_PROTO_IPCOMP:
+ return 0;
+
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid protocol id %d.\n", proto_id);
+ return -1;
+ }
+ /* NOT REACHED */
+}
+
+static int
+check_spi_size(proto_id, size)
+ int proto_id, size;
+{
+ switch (proto_id) {
+ case IPSECDOI_PROTO_ISAKMP:
+ if (size != 0) {
+ /* WARNING */
+ plog(LLV_WARNING, LOCATION, NULL,
+ "SPI size isn't zero, but IKE proposal.\n");
+ }
+ return 0;
+
+ case IPSECDOI_PROTO_IPSEC_AH:
+ case IPSECDOI_PROTO_IPSEC_ESP:
+ if (size != 4) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid SPI size=%d for IPSEC proposal.\n",
+ size);
+ return -1;
+ }
+ return 0;
+
+ case IPSECDOI_PROTO_IPCOMP:
+ if (size != 2 && size != 4) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid SPI size=%d for IPCOMP proposal.\n",
+ size);
+ return -1;
+ }
+ return 0;
+
+ default:
+ /* ??? */
+ return -1;
+ }
+ /* NOT REACHED */
+}
+
+/*
+ * check transform ID in ISAKMP.
+ */
+static int
+check_trns_isakmp(t_id)
+ int t_id;
+{
+ switch (t_id) {
+ case IPSECDOI_KEY_IKE:
+ return 0;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid transform-id=%u in proto_id=%u.\n",
+ t_id, IPSECDOI_KEY_IKE);
+ return -1;
+ }
+ /* NOT REACHED */
+}
+
+/*
+ * check transform ID in AH.
+ */
+static int
+check_trns_ah(t_id)
+ int t_id;
+{
+ switch (t_id) {
+ case IPSECDOI_AH_MD5:
+ case IPSECDOI_AH_SHA:
+ case IPSECDOI_AH_SHA256:
+ case IPSECDOI_AH_SHA384:
+ case IPSECDOI_AH_SHA512:
+ return 0;
+ case IPSECDOI_AH_DES:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "not support transform-id=%u in AH.\n", t_id);
+ return -1;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid transform-id=%u in AH.\n", t_id);
+ return -1;
+ }
+ /* NOT REACHED */
+}
+
+/*
+ * check transform ID in ESP.
+ */
+static int
+check_trns_esp(t_id)
+ int t_id;
+{
+ switch (t_id) {
+ case IPSECDOI_ESP_DES:
+ case IPSECDOI_ESP_3DES:
+ case IPSECDOI_ESP_NULL:
+ case IPSECDOI_ESP_RC5:
+ case IPSECDOI_ESP_CAST:
+ case IPSECDOI_ESP_BLOWFISH:
+ case IPSECDOI_ESP_AES:
+ case IPSECDOI_ESP_TWOFISH:
+ case IPSECDOI_ESP_CAMELLIA:
+ return 0;
+ case IPSECDOI_ESP_DES_IV32:
+ case IPSECDOI_ESP_DES_IV64:
+ case IPSECDOI_ESP_IDEA:
+ case IPSECDOI_ESP_3IDEA:
+ case IPSECDOI_ESP_RC4:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "not support transform-id=%u in ESP.\n", t_id);
+ return -1;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid transform-id=%u in ESP.\n", t_id);
+ return -1;
+ }
+ /* NOT REACHED */
+}
+
+/*
+ * check transform ID in IPCOMP.
+ */
+static int
+check_trns_ipcomp(t_id)
+ int t_id;
+{
+ switch (t_id) {
+ case IPSECDOI_IPCOMP_OUI:
+ case IPSECDOI_IPCOMP_DEFLATE:
+ case IPSECDOI_IPCOMP_LZS:
+ return 0;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid transform-id=%u in IPCOMP.\n", t_id);
+ return -1;
+ }
+ /* NOT REACHED */
+}
+
+/*
+ * check data attributes in IKE.
+ */
+static int
+check_attr_isakmp(trns)
+ struct isakmp_pl_t *trns;
+{
+ struct isakmp_data *d;
+ int tlen;
+ int flag, type;
+ u_int16_t lorv;
+
+ tlen = ntohs(trns->h.len) - sizeof(struct isakmp_pl_t);
+ d = (struct isakmp_data *)((caddr_t)trns + sizeof(struct isakmp_pl_t));
+
+ while (tlen > 0) {
+ type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
+ flag = ntohs(d->type) & ISAKMP_GEN_MASK;
+ lorv = ntohs(d->lorv);
+
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "type=%s, flag=0x%04x, lorv=%s\n",
+ s_oakley_attr(type), flag,
+ s_oakley_attr_v(type, lorv));
+
+ /*
+ * some of the attributes must be encoded in TV.
+ * see RFC2409 Appendix A "Attribute Classes".
+ */
+ switch (type) {
+ case OAKLEY_ATTR_ENC_ALG:
+ case OAKLEY_ATTR_HASH_ALG:
+ case OAKLEY_ATTR_AUTH_METHOD:
+ case OAKLEY_ATTR_GRP_DESC:
+ case OAKLEY_ATTR_GRP_TYPE:
+ case OAKLEY_ATTR_SA_LD_TYPE:
+ case OAKLEY_ATTR_PRF:
+ case OAKLEY_ATTR_KEY_LEN:
+ case OAKLEY_ATTR_FIELD_SIZE:
+ if (!flag) { /* TLV*/
+ plog(LLV_ERROR, LOCATION, NULL,
+ "oakley attribute %d must be TV.\n",
+ type);
+ return -1;
+ }
+ break;
+ }
+
+ /* sanity check for TLV. length must be specified. */
+ if (!flag && lorv == 0) { /*TLV*/
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid length %d for TLV attribute %d.\n",
+ lorv, type);
+ return -1;
+ }
+
+ switch (type) {
+ case OAKLEY_ATTR_ENC_ALG:
+ if (!alg_oakley_encdef_ok(lorv)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalied encryption algorithm=%d.\n",
+ lorv);
+ return -1;
+ }
+ break;
+
+ case OAKLEY_ATTR_HASH_ALG:
+ if (!alg_oakley_hashdef_ok(lorv)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalied hash algorithm=%d.\n",
+ lorv);
+ return -1;
+ }
+ break;
+
+ case OAKLEY_ATTR_AUTH_METHOD:
+ switch (lorv) {
+ case OAKLEY_ATTR_AUTH_METHOD_PSKEY:
+ case OAKLEY_ATTR_AUTH_METHOD_RSASIG:
+#ifdef ENABLE_HYBRID
+ case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I:
+ case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I:
+#endif
+#if defined(ENABLE_HYBRID) || defined(HAVE_GSSAPI)
+ /* These two authentication method IDs overlap. */
+ case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I:
+ /*case OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB:*/
+#endif
+ break;
+ case OAKLEY_ATTR_AUTH_METHOD_DSSSIG:
+#ifdef ENABLE_HYBRID
+ case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R:
+ case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R:
+ case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R:
+ case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I:
+ case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R:
+ case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I:
+ case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R:
+ case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_I:
+ case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_R:
+ case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_I:
+ case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_R:
+#endif
+ case OAKLEY_ATTR_AUTH_METHOD_RSAENC:
+ case OAKLEY_ATTR_AUTH_METHOD_RSAREV:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "auth method %s isn't supported.\n",
+ s_oakley_attr_method(lorv));
+ return -1;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid auth method %d.\n",
+ lorv);
+ return -1;
+ }
+ break;
+
+ case OAKLEY_ATTR_GRP_DESC:
+ if (!alg_oakley_dhdef_ok(lorv)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid DH group %d.\n",
+ lorv);
+ return -1;
+ }
+ break;
+
+ case OAKLEY_ATTR_GRP_TYPE:
+ switch (lorv) {
+ case OAKLEY_ATTR_GRP_TYPE_MODP:
+ break;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "unsupported DH group type %d.\n",
+ lorv);
+ return -1;
+ }
+ break;
+
+ case OAKLEY_ATTR_GRP_PI:
+ case OAKLEY_ATTR_GRP_GEN_ONE:
+ /* sanity checks? */
+ break;
+
+ case OAKLEY_ATTR_GRP_GEN_TWO:
+ case OAKLEY_ATTR_GRP_CURVE_A:
+ case OAKLEY_ATTR_GRP_CURVE_B:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "attr type=%u isn't supported.\n", type);
+ return -1;
+
+ case OAKLEY_ATTR_SA_LD_TYPE:
+ switch (lorv) {
+ case OAKLEY_ATTR_SA_LD_TYPE_SEC:
+ case OAKLEY_ATTR_SA_LD_TYPE_KB:
+ break;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid life type %d.\n", lorv);
+ return -1;
+ }
+ break;
+
+ case OAKLEY_ATTR_SA_LD:
+ /* should check the value */
+ break;
+
+ case OAKLEY_ATTR_PRF:
+ case OAKLEY_ATTR_KEY_LEN:
+ break;
+
+ case OAKLEY_ATTR_FIELD_SIZE:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "attr type=%u isn't supported.\n", type);
+ return -1;
+
+ case OAKLEY_ATTR_GRP_ORDER:
+ break;
+
+ case OAKLEY_ATTR_GSS_ID:
+ break;
+
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid attribute type %d.\n", type);
+ return -1;
+ }
+
+ if (flag) {
+ tlen -= sizeof(*d);
+ d = (struct isakmp_data *)((char *)d
+ + sizeof(*d));
+ } else {
+ tlen -= (sizeof(*d) + lorv);
+ d = (struct isakmp_data *)((char *)d
+ + sizeof(*d) + lorv);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * check data attributes in IPSEC AH/ESP.
+ */
+static int
+check_attr_ah(trns)
+ struct isakmp_pl_t *trns;
+{
+ return check_attr_ipsec(IPSECDOI_PROTO_IPSEC_AH, trns);
+}
+
+static int
+check_attr_esp(trns)
+ struct isakmp_pl_t *trns;
+{
+ return check_attr_ipsec(IPSECDOI_PROTO_IPSEC_ESP, trns);
+}
+
+static int
+check_attr_ipsec(proto_id, trns)
+ int proto_id;
+ struct isakmp_pl_t *trns;
+{
+ struct isakmp_data *d;
+ int tlen;
+ int flag, type = 0;
+ u_int16_t lorv;
+ int attrseen[16]; /* XXX magic number */
+
+ tlen = ntohs(trns->h.len) - sizeof(struct isakmp_pl_t);
+ d = (struct isakmp_data *)((caddr_t)trns + sizeof(struct isakmp_pl_t));
+ memset(attrseen, 0, sizeof(attrseen));
+
+ while (tlen > 0) {
+ type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
+ flag = ntohs(d->type) & ISAKMP_GEN_MASK;
+ lorv = ntohs(d->lorv);
+
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "type=%s, flag=0x%04x, lorv=%s\n",
+ s_ipsecdoi_attr(type), flag,
+ s_ipsecdoi_attr_v(type, lorv));
+
+ if (type < sizeof(attrseen)/sizeof(attrseen[0]))
+ attrseen[type]++;
+
+ switch (type) {
+ case IPSECDOI_ATTR_ENC_MODE:
+ if (! flag) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "must be TV when ENC_MODE.\n");
+ return -1;
+ }
+
+ switch (lorv) {
+ case IPSECDOI_ATTR_ENC_MODE_TUNNEL:
+ case IPSECDOI_ATTR_ENC_MODE_TRNS:
+ break;
+#ifdef ENABLE_NATT
+ case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_RFC:
+ case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC:
+ case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_DRAFT:
+ case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_DRAFT:
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "UDP encapsulation requested\n");
+ break;
+#endif
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid encryption mode=%u.\n",
+ lorv);
+ return -1;
+ }
+ break;
+
+ case IPSECDOI_ATTR_AUTH:
+ if (! flag) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "must be TV when AUTH.\n");
+ return -1;
+ }
+
+ switch (lorv) {
+ case IPSECDOI_ATTR_AUTH_HMAC_MD5:
+ if (proto_id == IPSECDOI_PROTO_IPSEC_AH &&
+ trns->t_id != IPSECDOI_AH_MD5) {
+ahmismatch:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "auth algorithm %u conflicts "
+ "with transform %u.\n",
+ lorv, trns->t_id);
+ return -1;
+ }
+ break;
+ case IPSECDOI_ATTR_AUTH_HMAC_SHA1:
+ if (proto_id == IPSECDOI_PROTO_IPSEC_AH) {
+ if (trns->t_id != IPSECDOI_AH_SHA)
+ goto ahmismatch;
+ }
+ break;
+ case IPSECDOI_ATTR_AUTH_HMAC_SHA2_256:
+ if (proto_id == IPSECDOI_PROTO_IPSEC_AH) {
+ if (trns->t_id != IPSECDOI_AH_SHA256)
+ goto ahmismatch;
+ }
+ break;
+ case IPSECDOI_ATTR_AUTH_HMAC_SHA2_384:
+ if (proto_id == IPSECDOI_PROTO_IPSEC_AH) {
+ if (trns->t_id != IPSECDOI_AH_SHA384)
+ goto ahmismatch;
+ }
+ break;
+ case IPSECDOI_ATTR_AUTH_HMAC_SHA2_512:
+ if (proto_id == IPSECDOI_PROTO_IPSEC_AH) {
+ if (trns->t_id != IPSECDOI_AH_SHA512)
+ goto ahmismatch;
+ }
+ break;
+ case IPSECDOI_ATTR_AUTH_DES_MAC:
+ case IPSECDOI_ATTR_AUTH_KPDK:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "auth algorithm %u isn't supported.\n",
+ lorv);
+ return -1;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid auth algorithm=%u.\n",
+ lorv);
+ return -1;
+ }
+ break;
+
+ case IPSECDOI_ATTR_SA_LD_TYPE:
+ if (! flag) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "must be TV when LD_TYPE.\n");
+ return -1;
+ }
+
+ switch (lorv) {
+ case IPSECDOI_ATTR_SA_LD_TYPE_SEC:
+ case IPSECDOI_ATTR_SA_LD_TYPE_KB:
+ break;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid life type %d.\n", lorv);
+ return -1;
+ }
+ break;
+
+ case IPSECDOI_ATTR_SA_LD:
+ if (flag) {
+ /* i.e. ISAKMP_GEN_TV */
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "life duration was in TLV.\n");
+ } else {
+ /* i.e. ISAKMP_GEN_TLV */
+ if (lorv == 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid length of LD\n");
+ return -1;
+ }
+ }
+ break;
+
+ case IPSECDOI_ATTR_GRP_DESC:
+ if (! flag) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "must be TV when GRP_DESC.\n");
+ return -1;
+ }
+
+ if (!alg_oakley_dhdef_ok(lorv)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid group description=%u.\n",
+ lorv);
+ return -1;
+ }
+ break;
+
+ case IPSECDOI_ATTR_KEY_LENGTH:
+ if (! flag) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "must be TV when KEY_LENGTH.\n");
+ return -1;
+ }
+ break;
+
+#ifdef HAVE_SECCTX
+ case IPSECDOI_ATTR_SECCTX:
+ if (flag) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "SECCTX must be in TLV.\n");
+ return -1;
+ }
+ break;
+#endif
+
+ case IPSECDOI_ATTR_KEY_ROUNDS:
+ case IPSECDOI_ATTR_COMP_DICT_SIZE:
+ case IPSECDOI_ATTR_COMP_PRIVALG:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "attr type=%u isn't supported.\n", type);
+ return -1;
+
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid attribute type %d.\n", type);
+ return -1;
+ }
+
+ if (flag) {
+ tlen -= sizeof(*d);
+ d = (struct isakmp_data *)((char *)d
+ + sizeof(*d));
+ } else {
+ tlen -= (sizeof(*d) + lorv);
+ d = (struct isakmp_data *)((caddr_t)d
+ + sizeof(*d) + lorv);
+ }
+ }
+
+ if (proto_id == IPSECDOI_PROTO_IPSEC_AH &&
+ !attrseen[IPSECDOI_ATTR_AUTH]) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "attr AUTH must be present for AH.\n");
+ return -1;
+ }
+
+ if (proto_id == IPSECDOI_PROTO_IPSEC_ESP &&
+ trns->t_id == IPSECDOI_ESP_NULL &&
+ !attrseen[IPSECDOI_ATTR_AUTH]) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "attr AUTH must be present for ESP NULL encryption.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+check_attr_ipcomp(trns)
+ struct isakmp_pl_t *trns;
+{
+ struct isakmp_data *d;
+ int tlen;
+ int flag, type = 0;
+ u_int16_t lorv;
+ int attrseen[16]; /* XXX magic number */
+
+ tlen = ntohs(trns->h.len) - sizeof(struct isakmp_pl_t);
+ d = (struct isakmp_data *)((caddr_t)trns + sizeof(struct isakmp_pl_t));
+ memset(attrseen, 0, sizeof(attrseen));
+
+ while (tlen > 0) {
+ type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
+ flag = ntohs(d->type) & ISAKMP_GEN_MASK;
+ lorv = ntohs(d->lorv);
+
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "type=%d, flag=0x%04x, lorv=0x%04x\n",
+ type, flag, lorv);
+
+ if (type < sizeof(attrseen)/sizeof(attrseen[0]))
+ attrseen[type]++;
+
+ switch (type) {
+ case IPSECDOI_ATTR_ENC_MODE:
+ if (! flag) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "must be TV when ENC_MODE.\n");
+ return -1;
+ }
+
+ switch (lorv) {
+ case IPSECDOI_ATTR_ENC_MODE_TUNNEL:
+ case IPSECDOI_ATTR_ENC_MODE_TRNS:
+ break;
+#ifdef ENABLE_NATT
+ case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_RFC:
+ case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC:
+ case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_DRAFT:
+ case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_DRAFT:
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "UDP encapsulation requested\n");
+ break;
+#endif
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid encryption mode=%u.\n",
+ lorv);
+ return -1;
+ }
+ break;
+
+ case IPSECDOI_ATTR_SA_LD_TYPE:
+ if (! flag) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "must be TV when LD_TYPE.\n");
+ return -1;
+ }
+
+ switch (lorv) {
+ case IPSECDOI_ATTR_SA_LD_TYPE_SEC:
+ case IPSECDOI_ATTR_SA_LD_TYPE_KB:
+ break;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid life type %d.\n", lorv);
+ return -1;
+ }
+ break;
+
+ case IPSECDOI_ATTR_SA_LD:
+ if (flag) {
+ /* i.e. ISAKMP_GEN_TV */
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "life duration was in TLV.\n");
+ } else {
+ /* i.e. ISAKMP_GEN_TLV */
+ if (lorv == 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid length of LD\n");
+ return -1;
+ }
+ }
+ break;
+
+ case IPSECDOI_ATTR_GRP_DESC:
+ if (! flag) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "must be TV when GRP_DESC.\n");
+ return -1;
+ }
+
+ if (!alg_oakley_dhdef_ok(lorv)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid group description=%u.\n",
+ lorv);
+ return -1;
+ }
+ break;
+
+ case IPSECDOI_ATTR_AUTH:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid attr type=%u.\n", type);
+ return -1;
+
+ case IPSECDOI_ATTR_KEY_LENGTH:
+ case IPSECDOI_ATTR_KEY_ROUNDS:
+ case IPSECDOI_ATTR_COMP_DICT_SIZE:
+ case IPSECDOI_ATTR_COMP_PRIVALG:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "attr type=%u isn't supported.\n", type);
+ return -1;
+
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid attribute type %d.\n", type);
+ return -1;
+ }
+
+ if (flag) {
+ tlen -= sizeof(*d);
+ d = (struct isakmp_data *)((char *)d
+ + sizeof(*d));
+ } else {
+ tlen -= (sizeof(*d) + lorv);
+ d = (struct isakmp_data *)((caddr_t)d
+ + sizeof(*d) + lorv);
+ }
+ }
+
+#if 0
+ if (proto_id == IPSECDOI_PROTO_IPCOMP &&
+ !attrseen[IPSECDOI_ATTR_AUTH]) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "attr AUTH must be present for AH.\n", type);
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+/* %%% */
+/*
+ * create phase1 proposal from remote configuration.
+ * NOT INCLUDING isakmp general header of SA payload
+ */
+vchar_t *
+ipsecdoi_setph1proposal(rmconf, props)
+ struct remoteconf *rmconf;
+ struct isakmpsa *props;
+{
+ vchar_t *mysa;
+ int sablen;
+
+ /* count total size of SA minus isakmp general header */
+ /* not including isakmp general header of SA payload */
+ sablen = sizeof(struct ipsecdoi_sa_b);
+ sablen += setph1prop(props, NULL);
+
+ mysa = vmalloc(sablen);
+ if (mysa == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate my sa buffer\n");
+ return NULL;
+ }
+
+ /* create SA payload */
+ /* not including isakmp general header */
+ ((struct ipsecdoi_sa_b *)mysa->v)->doi = htonl(rmconf->doitype);
+ ((struct ipsecdoi_sa_b *)mysa->v)->sit = htonl(rmconf->sittype);
+
+ (void)setph1prop(props, mysa->v + sizeof(struct ipsecdoi_sa_b));
+
+ return mysa;
+}
+
+static int
+setph1prop(props, buf)
+ struct isakmpsa *props;
+ caddr_t buf;
+{
+ struct isakmp_pl_p *prop = NULL;
+ struct isakmpsa *s = NULL;
+ int proplen, trnslen;
+ u_int8_t *np_t; /* pointer next trns type in previous header */
+ int trns_num;
+ caddr_t p = buf;
+
+ proplen = sizeof(*prop);
+ if (buf) {
+ /* create proposal */
+ prop = (struct isakmp_pl_p *)p;
+ prop->h.np = ISAKMP_NPTYPE_NONE;
+ prop->p_no = props->prop_no;
+ prop->proto_id = IPSECDOI_PROTO_ISAKMP;
+ prop->spi_size = 0;
+ p += sizeof(*prop);
+ }
+
+ np_t = NULL;
+ trns_num = 0;
+
+ for (s = props; s != NULL; s = s->next) {
+ if (np_t)
+ *np_t = ISAKMP_NPTYPE_T;
+
+ trnslen = setph1trns(s, p);
+ proplen += trnslen;
+ if (buf) {
+ /* save buffer to pre-next payload */
+ np_t = &((struct isakmp_pl_t *)p)->h.np;
+ p += trnslen;
+
+ /* count up transform length */
+ trns_num++;
+ }
+ }
+
+ /* update proposal length */
+ if (buf) {
+ prop->h.len = htons(proplen);
+ prop->num_t = trns_num;
+ }
+
+ return proplen;
+}
+
+static int
+setph1trns(sa, buf)
+ struct isakmpsa *sa;
+ caddr_t buf;
+{
+ struct isakmp_pl_t *trns = NULL;
+ int trnslen, attrlen;
+ caddr_t p = buf;
+
+ trnslen = sizeof(*trns);
+ if (buf) {
+ /* create transform */
+ trns = (struct isakmp_pl_t *)p;
+ trns->h.np = ISAKMP_NPTYPE_NONE;
+ trns->t_no = sa->trns_no;
+ trns->t_id = IPSECDOI_KEY_IKE;
+ p += sizeof(*trns);
+ }
+
+ attrlen = setph1attr(sa, p);
+ trnslen += attrlen;
+ if (buf)
+ p += attrlen;
+
+ if (buf)
+ trns->h.len = htons(trnslen);
+
+ return trnslen;
+}
+
+static int
+setph1attr(sa, buf)
+ struct isakmpsa *sa;
+ caddr_t buf;
+{
+ caddr_t p = buf;
+ int attrlen = 0;
+
+ if (sa->lifetime) {
+ u_int32_t lifetime = htonl((u_int32_t)sa->lifetime);
+
+ attrlen += sizeof(struct isakmp_data)
+ + sizeof(struct isakmp_data);
+ if (sa->lifetime > 0xffff)
+ attrlen += sizeof(lifetime);
+ if (buf) {
+ p = isakmp_set_attr_l(p, OAKLEY_ATTR_SA_LD_TYPE,
+ OAKLEY_ATTR_SA_LD_TYPE_SEC);
+ if (sa->lifetime > 0xffff) {
+ p = isakmp_set_attr_v(p, OAKLEY_ATTR_SA_LD,
+ (caddr_t)&lifetime,
+ sizeof(lifetime));
+ } else {
+ p = isakmp_set_attr_l(p, OAKLEY_ATTR_SA_LD,
+ sa->lifetime);
+ }
+ }
+ }
+
+ if (sa->lifebyte) {
+ u_int32_t lifebyte = htonl((u_int32_t)sa->lifebyte);
+
+ attrlen += sizeof(struct isakmp_data)
+ + sizeof(struct isakmp_data);
+ if (sa->lifebyte > 0xffff)
+ attrlen += sizeof(lifebyte);
+ if (buf) {
+ p = isakmp_set_attr_l(p, OAKLEY_ATTR_SA_LD_TYPE,
+ OAKLEY_ATTR_SA_LD_TYPE_KB);
+ if (sa->lifebyte > 0xffff) {
+ p = isakmp_set_attr_v(p, OAKLEY_ATTR_SA_LD,
+ (caddr_t)&lifebyte,
+ sizeof(lifebyte));
+ } else {
+ p = isakmp_set_attr_l(p, OAKLEY_ATTR_SA_LD,
+ sa->lifebyte);
+ }
+ }
+ }
+
+ if (sa->enctype) {
+ attrlen += sizeof(struct isakmp_data);
+ if (buf)
+ p = isakmp_set_attr_l(p, OAKLEY_ATTR_ENC_ALG, sa->enctype);
+ }
+ if (sa->encklen) {
+ attrlen += sizeof(struct isakmp_data);
+ if (buf)
+ p = isakmp_set_attr_l(p, OAKLEY_ATTR_KEY_LEN, sa->encklen);
+ }
+ if (sa->authmethod) {
+ int authmethod;
+
+ authmethod = isakmpsa_switch_authmethod(sa->authmethod);
+ authmethod &= 0xffff;
+ attrlen += sizeof(struct isakmp_data);
+ if (buf)
+ p = isakmp_set_attr_l(p, OAKLEY_ATTR_AUTH_METHOD, authmethod);
+ }
+ if (sa->hashtype) {
+ attrlen += sizeof(struct isakmp_data);
+ if (buf)
+ p = isakmp_set_attr_l(p, OAKLEY_ATTR_HASH_ALG, sa->hashtype);
+ }
+ switch (sa->dh_group) {
+ case OAKLEY_ATTR_GRP_DESC_MODP768:
+ case OAKLEY_ATTR_GRP_DESC_MODP1024:
+ case OAKLEY_ATTR_GRP_DESC_MODP1536:
+ case OAKLEY_ATTR_GRP_DESC_MODP2048:
+ case OAKLEY_ATTR_GRP_DESC_MODP3072:
+ case OAKLEY_ATTR_GRP_DESC_MODP4096:
+ case OAKLEY_ATTR_GRP_DESC_MODP6144:
+ case OAKLEY_ATTR_GRP_DESC_MODP8192:
+ /* don't attach group type for known groups */
+ attrlen += sizeof(struct isakmp_data);
+ if (buf) {
+ p = isakmp_set_attr_l(p, OAKLEY_ATTR_GRP_DESC,
+ sa->dh_group);
+ }
+ break;
+ case OAKLEY_ATTR_GRP_DESC_EC2N155:
+ case OAKLEY_ATTR_GRP_DESC_EC2N185:
+ /* don't attach group type for known groups */
+ attrlen += sizeof(struct isakmp_data);
+ if (buf) {
+ p = isakmp_set_attr_l(p, OAKLEY_ATTR_GRP_TYPE,
+ OAKLEY_ATTR_GRP_TYPE_EC2N);
+ }
+ break;
+ case 0:
+ default:
+ break;
+ }
+
+#ifdef HAVE_GSSAPI
+ if (sa->authmethod == OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB &&
+ sa->gssid != NULL) {
+ attrlen += sizeof(struct isakmp_data);
+ /*
+ * Older versions of racoon just placed the ISO-Latin-1
+ * string on the wire directly. Check to see if we are
+ * configured to be compatible with this behavior. Otherwise,
+ * we encode the GSS ID as UTF-16LE for Windows 2000
+ * compatibility, which requires twice the number of octets.
+ */
+ if (lcconf->gss_id_enc == LC_GSSENC_LATIN1)
+ attrlen += sa->gssid->l;
+ else
+ attrlen += sa->gssid->l * 2;
+ if (buf) {
+ plog(LLV_DEBUG, LOCATION, NULL, "gss id attr: len %zu, "
+ "val '%.*s'\n", sa->gssid->l, (int)sa->gssid->l,
+ sa->gssid->v);
+ if (lcconf->gss_id_enc == LC_GSSENC_LATIN1) {
+ p = isakmp_set_attr_v(p, OAKLEY_ATTR_GSS_ID,
+ (caddr_t)sa->gssid->v,
+ sa->gssid->l);
+ } else {
+ size_t dstleft = sa->gssid->l * 2;
+ size_t srcleft = sa->gssid->l;
+ const char *src = (const char *)sa->gssid->v;
+ char *odst, *dst = racoon_malloc(dstleft);
+ iconv_t cd;
+ size_t rv;
+
+ cd = iconv_open("utf-16le", "latin1");
+ if (cd == (iconv_t) -1) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "unable to initialize "
+ "latin1 -> utf-16le "
+ "converstion descriptor: %s\n",
+ strerror(errno));
+ attrlen -= sa->gssid->l * 2;
+ goto gssid_done;
+ }
+ odst = dst;
+ rv = iconv(cd, (__iconv_const char **)&src,
+ &srcleft, &dst, &dstleft);
+ if (rv != 0) {
+ if (rv == -1) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "unable to convert GSS ID "
+ "from latin1 -> utf-16le: "
+ "%s\n", strerror(errno));
+ } else {
+ /* should never happen */
+ plog(LLV_ERROR, LOCATION, NULL,
+ "%zd character%s in GSS ID "
+ "cannot be represented "
+ "in utf-16le\n",
+ rv, rv == 1 ? "" : "s");
+ }
+ (void) iconv_close(cd);
+ attrlen -= sa->gssid->l * 2;
+ goto gssid_done;
+ }
+ (void) iconv_close(cd);
+
+ /* XXX Check srcleft and dstleft? */
+
+ p = isakmp_set_attr_v(p, OAKLEY_ATTR_GSS_ID,
+ odst, sa->gssid->l * 2);
+
+ racoon_free(odst);
+ }
+ }
+ }
+ gssid_done:
+#endif /* HAVE_GSSAPI */
+
+ return attrlen;
+}
+
+static vchar_t *
+setph2proposal0(iph2, pp, pr)
+ const struct ph2handle *iph2;
+ const struct saprop *pp;
+ const struct saproto *pr;
+{
+ vchar_t *p;
+ struct isakmp_pl_p *prop;
+ struct isakmp_pl_t *trns;
+ struct satrns *tr;
+ int attrlen;
+ size_t trnsoff;
+ caddr_t x0, x;
+ u_int8_t *np_t; /* pointer next trns type in previous header */
+ const u_int8_t *spi;
+#ifdef HAVE_SECCTX
+ int truectxlen = 0;
+#endif
+
+ p = vmalloc(sizeof(*prop) + sizeof(pr->spi));
+ if (p == NULL)
+ return NULL;
+
+ /* create proposal */
+ prop = (struct isakmp_pl_p *)p->v;
+ prop->h.np = ISAKMP_NPTYPE_NONE;
+ prop->p_no = pp->prop_no;
+ prop->proto_id = pr->proto_id;
+ prop->num_t = 1;
+
+ spi = (const u_int8_t *)&pr->spi;
+ switch (pr->proto_id) {
+ case IPSECDOI_PROTO_IPCOMP:
+ /*
+ * draft-shacham-ippcp-rfc2393bis-05.txt:
+ * construct 16bit SPI (CPI).
+ * XXX we may need to provide a configuration option to
+ * generate 32bit SPI. otherwise we cannot interoeprate
+ * with nodes that uses 32bit SPI, in case we are initiator.
+ */
+ prop->spi_size = sizeof(u_int16_t);
+ spi += sizeof(pr->spi) - sizeof(u_int16_t);
+ p->l -= sizeof(pr->spi);
+ p->l += sizeof(u_int16_t);
+ break;
+ default:
+ prop->spi_size = sizeof(pr->spi);
+ break;
+ }
+ memcpy(prop + 1, spi, prop->spi_size);
+
+ /* create transform */
+ trnsoff = sizeof(*prop) + prop->spi_size;
+ np_t = NULL;
+
+ for (tr = pr->head; tr; tr = tr->next) {
+
+ switch (pr->proto_id) {
+ case IPSECDOI_PROTO_IPSEC_ESP:
+ /*
+ * don't build a null encryption
+ * with no authentication transform.
+ */
+ if (tr->trns_id == IPSECDOI_ESP_NULL &&
+ tr->authtype == IPSECDOI_ATTR_AUTH_NONE)
+ continue;
+ break;
+ }
+
+ if (np_t) {
+ *np_t = ISAKMP_NPTYPE_T;
+ prop->num_t++;
+ }
+
+ /* get attribute length */
+ attrlen = 0;
+ if (pp->lifetime) {
+ attrlen += sizeof(struct isakmp_data)
+ + sizeof(struct isakmp_data);
+ if (pp->lifetime > 0xffff)
+ attrlen += sizeof(u_int32_t);
+ }
+ if (pp->lifebyte && pp->lifebyte != IPSECDOI_ATTR_SA_LD_KB_MAX) {
+ attrlen += sizeof(struct isakmp_data)
+ + sizeof(struct isakmp_data);
+ if (pp->lifebyte > 0xffff)
+ attrlen += sizeof(u_int32_t);
+ }
+ attrlen += sizeof(struct isakmp_data); /* enc mode */
+ if (tr->encklen)
+ attrlen += sizeof(struct isakmp_data);
+
+ switch (pr->proto_id) {
+ case IPSECDOI_PROTO_IPSEC_ESP:
+ /* non authentication mode ? */
+ if (tr->authtype != IPSECDOI_ATTR_AUTH_NONE)
+ attrlen += sizeof(struct isakmp_data);
+ break;
+ case IPSECDOI_PROTO_IPSEC_AH:
+ if (tr->authtype == IPSECDOI_ATTR_AUTH_NONE) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "no authentication algorithm found "
+ "but protocol is AH.\n");
+ vfree(p);
+ return NULL;
+ }
+ attrlen += sizeof(struct isakmp_data);
+ break;
+ case IPSECDOI_PROTO_IPCOMP:
+ break;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid protocol: %d\n", pr->proto_id);
+ vfree(p);
+ return NULL;
+ }
+
+ if (alg_oakley_dhdef_ok(iph2->sainfo->pfs_group))
+ attrlen += sizeof(struct isakmp_data);
+
+#ifdef HAVE_SECCTX
+ /* ctx_str is defined as char ctx_str[MAX_CTXSTR_SIZ].
+ * The string may be smaller than MAX_CTXSTR_SIZ.
+ */
+ if (*pp->sctx.ctx_str) {
+ truectxlen = sizeof(struct security_ctx) -
+ (MAX_CTXSTR_SIZE - pp->sctx.ctx_strlen);
+ attrlen += sizeof(struct isakmp_data) + truectxlen;
+ }
+#endif /* HAVE_SECCTX */
+
+ p = vrealloc(p, p->l + sizeof(*trns) + attrlen);
+ if (p == NULL)
+ return NULL;
+ prop = (struct isakmp_pl_p *)p->v;
+
+ /* set transform's values */
+ trns = (struct isakmp_pl_t *)(p->v + trnsoff);
+ trns->h.np = ISAKMP_NPTYPE_NONE;
+ trns->t_no = tr->trns_no;
+ trns->t_id = tr->trns_id;
+
+ /* set attributes */
+ x = x0 = p->v + trnsoff + sizeof(*trns);
+
+ if (pp->lifetime) {
+ x = isakmp_set_attr_l(x, IPSECDOI_ATTR_SA_LD_TYPE,
+ IPSECDOI_ATTR_SA_LD_TYPE_SEC);
+ if (pp->lifetime > 0xffff) {
+ u_int32_t v = htonl((u_int32_t)pp->lifetime);
+ x = isakmp_set_attr_v(x, IPSECDOI_ATTR_SA_LD,
+ (caddr_t)&v, sizeof(v));
+ } else {
+ x = isakmp_set_attr_l(x, IPSECDOI_ATTR_SA_LD,
+ pp->lifetime);
+ }
+ }
+
+ if (pp->lifebyte && pp->lifebyte != IPSECDOI_ATTR_SA_LD_KB_MAX) {
+ x = isakmp_set_attr_l(x, IPSECDOI_ATTR_SA_LD_TYPE,
+ IPSECDOI_ATTR_SA_LD_TYPE_KB);
+ if (pp->lifebyte > 0xffff) {
+ u_int32_t v = htonl((u_int32_t)pp->lifebyte);
+ x = isakmp_set_attr_v(x, IPSECDOI_ATTR_SA_LD,
+ (caddr_t)&v, sizeof(v));
+ } else {
+ x = isakmp_set_attr_l(x, IPSECDOI_ATTR_SA_LD,
+ pp->lifebyte);
+ }
+ }
+
+ x = isakmp_set_attr_l(x, IPSECDOI_ATTR_ENC_MODE, pr->encmode);
+
+ if (tr->encklen)
+ x = isakmp_set_attr_l(x, IPSECDOI_ATTR_KEY_LENGTH, tr->encklen);
+
+ /* mandatory check has done above. */
+ if ((pr->proto_id == IPSECDOI_PROTO_IPSEC_ESP && tr->authtype != IPSECDOI_ATTR_AUTH_NONE)
+ || pr->proto_id == IPSECDOI_PROTO_IPSEC_AH)
+ x = isakmp_set_attr_l(x, IPSECDOI_ATTR_AUTH, tr->authtype);
+
+ if (alg_oakley_dhdef_ok(iph2->sainfo->pfs_group))
+ x = isakmp_set_attr_l(x, IPSECDOI_ATTR_GRP_DESC,
+ iph2->sainfo->pfs_group);
+
+#ifdef HAVE_SECCTX
+ if (*pp->sctx.ctx_str) {
+ struct security_ctx secctx;
+ secctx = pp->sctx;
+ secctx.ctx_strlen = htons(pp->sctx.ctx_strlen);
+ x = isakmp_set_attr_v(x, IPSECDOI_ATTR_SECCTX,
+ (caddr_t)&secctx, truectxlen);
+ }
+#endif
+ /* update length of this transform. */
+ trns = (struct isakmp_pl_t *)(p->v + trnsoff);
+ trns->h.len = htons(sizeof(*trns) + attrlen);
+
+ /* save buffer to pre-next payload */
+ np_t = &trns->h.np;
+
+ trnsoff += (sizeof(*trns) + attrlen);
+ }
+
+ if (np_t == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "no suitable proposal was created.\n");
+ return NULL;
+ }
+
+ /* update length of this protocol. */
+ prop->h.len = htons(p->l);
+
+ return p;
+}
+
+/*
+ * create phase2 proposal from policy configuration.
+ * NOT INCLUDING isakmp general header of SA payload.
+ * This function is called by initiator only.
+ */
+int
+ipsecdoi_setph2proposal(iph2)
+ struct ph2handle *iph2;
+{
+ struct saprop *proposal, *a;
+ struct saproto *b = NULL;
+ vchar_t *q;
+ struct ipsecdoi_sa_b *sab;
+ struct isakmp_pl_p *prop;
+ size_t propoff; /* for previous field of type of next payload. */
+
+ proposal = iph2->proposal;
+
+ iph2->sa = vmalloc(sizeof(*sab));
+ if (iph2->sa == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate my sa buffer\n");
+ return -1;
+ }
+
+ /* create SA payload */
+ sab = (struct ipsecdoi_sa_b *)iph2->sa->v;
+ sab->doi = htonl(IPSEC_DOI);
+ sab->sit = htonl(IPSECDOI_SIT_IDENTITY_ONLY); /* XXX configurable ? */
+
+ prop = NULL;
+ propoff = 0;
+ for (a = proposal; a; a = a->next) {
+ for (b = a->head; b; b = b->next) {
+#ifdef ENABLE_NATT
+ if (iph2->ph1->natt_flags & NAT_DETECTED) {
+ int udp_diff = iph2->ph1->natt_options->mode_udp_diff;
+ plog (LLV_INFO, LOCATION, NULL,
+ "NAT detected -> UDP encapsulation "
+ "(ENC_MODE %d->%d).\n",
+ b->encmode,
+ b->encmode+udp_diff);
+ /* Tunnel -> UDP-Tunnel, Transport -> UDP_Transport */
+ b->encmode += udp_diff;
+ b->udp_encap = 1;
+ }
+#endif
+
+ q = setph2proposal0(iph2, a, b);
+ if (q == NULL) {
+ VPTRINIT(iph2->sa);
+ return -1;
+ }
+
+ iph2->sa = vrealloc(iph2->sa, iph2->sa->l + q->l);
+ if (iph2->sa == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate my sa buffer\n");
+ if (q)
+ vfree(q);
+ return -1;
+ }
+ memcpy(iph2->sa->v + iph2->sa->l - q->l, q->v, q->l);
+ if (propoff != 0) {
+ prop = (struct isakmp_pl_p *)(iph2->sa->v +
+ propoff);
+ prop->h.np = ISAKMP_NPTYPE_P;
+ }
+ propoff = iph2->sa->l - q->l;
+
+ vfree(q);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * return 1 if all of the given protocols are transport mode.
+ */
+int
+ipsecdoi_transportmode(pp)
+ struct saprop *pp;
+{
+ struct saproto *pr = NULL;
+
+ for (; pp; pp = pp->next) {
+ for (pr = pp->head; pr; pr = pr->next) {
+ if (pr->encmode != IPSECDOI_ATTR_ENC_MODE_TRNS &&
+ pr->encmode != IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC &&
+ pr->encmode != IPSECDOI_ATTR_ENC_MODE_UDPTRNS_DRAFT)
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int
+ipsecdoi_get_defaultlifetime()
+{
+ return IPSECDOI_ATTR_SA_LD_SEC_DEFAULT;
+}
+
+int
+ipsecdoi_checkalgtypes(proto_id, enc, auth, comp)
+ int proto_id, enc, auth, comp;
+{
+#define TMPALGTYPE2STR(n) s_algtype(algclass_ipsec_##n, n)
+ switch (proto_id) {
+ case IPSECDOI_PROTO_IPSEC_ESP:
+ if (enc == 0 || comp != 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "illegal algorithm defined "
+ "ESP enc=%s auth=%s comp=%s.\n",
+ TMPALGTYPE2STR(enc),
+ TMPALGTYPE2STR(auth),
+ TMPALGTYPE2STR(comp));
+ return -1;
+ }
+ break;
+ case IPSECDOI_PROTO_IPSEC_AH:
+ if (enc != 0 || auth == 0 || comp != 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "illegal algorithm defined "
+ "AH enc=%s auth=%s comp=%s.\n",
+ TMPALGTYPE2STR(enc),
+ TMPALGTYPE2STR(auth),
+ TMPALGTYPE2STR(comp));
+ return -1;
+ }
+ break;
+ case IPSECDOI_PROTO_IPCOMP:
+ if (enc != 0 || auth != 0 || comp == 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "illegal algorithm defined "
+ "IPcomp enc=%s auth=%s comp=%s.\n",
+ TMPALGTYPE2STR(enc),
+ TMPALGTYPE2STR(auth),
+ TMPALGTYPE2STR(comp));
+ return -1;
+ }
+ break;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid ipsec protocol %d\n", proto_id);
+ return -1;
+ }
+#undef TMPALGTYPE2STR
+ return 0;
+}
+
+int
+ipproto2doi(proto)
+ int proto;
+{
+ switch (proto) {
+ case IPPROTO_AH:
+ return IPSECDOI_PROTO_IPSEC_AH;
+ case IPPROTO_ESP:
+ return IPSECDOI_PROTO_IPSEC_ESP;
+ case IPPROTO_IPCOMP:
+ return IPSECDOI_PROTO_IPCOMP;
+ }
+ return -1; /* XXX */
+}
+
+int
+doi2ipproto(proto)
+ int proto;
+{
+ switch (proto) {
+ case IPSECDOI_PROTO_IPSEC_AH:
+ return IPPROTO_AH;
+ case IPSECDOI_PROTO_IPSEC_ESP:
+ return IPPROTO_ESP;
+ case IPSECDOI_PROTO_IPCOMP:
+ return IPPROTO_IPCOMP;
+ }
+ return -1; /* XXX */
+}
+
+/*
+ * Check if a subnet id is valid for comparison
+ * with an address id ( address length mask )
+ * and compare them
+ * Return value
+ * = 0 for match
+ * = 1 for mismatch
+ */
+
+int
+ipsecdoi_subnetisaddr_v4( subnet, address )
+ const vchar_t *subnet;
+ const vchar_t *address;
+{
+ struct in_addr *mask;
+
+ if (address->l != sizeof(struct in_addr))
+ return 1;
+
+ if (subnet->l != (sizeof(struct in_addr)*2))
+ return 1;
+
+ mask = (struct in_addr*)(subnet->v + sizeof(struct in_addr));
+
+ if (mask->s_addr!=0xffffffff)
+ return 1;
+
+ return memcmp(subnet->v,address->v,address->l);
+}
+
+#ifdef INET6
+
+int
+ipsecdoi_subnetisaddr_v6( subnet, address )
+ const vchar_t *subnet;
+ const vchar_t *address;
+{
+ struct in6_addr *mask;
+ int i;
+
+ if (address->l != sizeof(struct in6_addr))
+ return 1;
+
+ if (subnet->l != (sizeof(struct in6_addr)*2))
+ return 1;
+
+ mask = (struct in6_addr*)(subnet->v + sizeof(struct in6_addr));
+
+ for (i=0; i<16; i++)
+ if(mask->s6_addr[i]!=0xff)
+ return 1;
+
+ return memcmp(subnet->v,address->v,address->l);
+}
+
+#endif
+
+/*
+ * Check and Compare two IDs
+ * - specify 0 for exact if wildcards are allowed
+ * Return value
+ * = 0 for match
+ * = 1 for misatch
+ * = -1 for integrity error
+ */
+
+int
+ipsecdoi_chkcmpids( idt, ids, exact )
+ const vchar_t *idt; /* id cmp target */
+ const vchar_t *ids; /* id cmp source */
+ int exact;
+{
+ struct ipsecdoi_id_b *id_bt;
+ struct ipsecdoi_id_b *id_bs;
+ vchar_t ident_t;
+ vchar_t ident_s;
+ int result;
+
+ /* handle wildcard IDs */
+
+ if (idt == NULL || ids == NULL)
+ {
+ if( !exact )
+ {
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "check and compare ids : values matched (ANONYMOUS)\n" );
+ return 0;
+ }
+ else
+ {
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "check and compare ids : value mismatch (ANONYMOUS)\n" );
+ return -1;
+ }
+ }
+
+ /* make sure the ids are of the same type */
+
+ id_bt = (struct ipsecdoi_id_b *) idt->v;
+ id_bs = (struct ipsecdoi_id_b *) ids->v;
+
+ ident_t.v = idt->v + sizeof(*id_bt);
+ ident_t.l = idt->l - sizeof(*id_bt);
+ ident_s.v = ids->v + sizeof(*id_bs);
+ ident_s.l = ids->l - sizeof(*id_bs);
+
+ if (id_bs->type != id_bt->type)
+ {
+ /*
+ * special exception for comparing
+ * address to subnet id types when
+ * the netmask is address length
+ */
+
+ if ((id_bs->type == IPSECDOI_ID_IPV4_ADDR)&&
+ (id_bt->type == IPSECDOI_ID_IPV4_ADDR_SUBNET)) {
+ result = ipsecdoi_subnetisaddr_v4(&ident_t,&ident_s);
+ goto cmpid_result;
+ }
+
+ if ((id_bs->type == IPSECDOI_ID_IPV4_ADDR_SUBNET)&&
+ (id_bt->type == IPSECDOI_ID_IPV4_ADDR)) {
+ result = ipsecdoi_subnetisaddr_v4(&ident_s,&ident_t);
+ goto cmpid_result;
+ }
+
+#ifdef INET6
+ if ((id_bs->type == IPSECDOI_ID_IPV6_ADDR)&&
+ (id_bt->type == IPSECDOI_ID_IPV6_ADDR_SUBNET)) {
+ result = ipsecdoi_subnetisaddr_v6(&ident_t,&ident_s);
+ goto cmpid_result;
+ }
+
+ if ((id_bs->type == IPSECDOI_ID_IPV6_ADDR_SUBNET)&&
+ (id_bt->type == IPSECDOI_ID_IPV6_ADDR)) {
+ result = ipsecdoi_subnetisaddr_v6(&ident_s,&ident_t);
+ goto cmpid_result;
+ }
+#endif
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "check and compare ids : id type mismatch %s != %s\n",
+ s_ipsecdoi_ident(id_bs->type),
+ s_ipsecdoi_ident(id_bt->type));
+
+ return 1;
+ }
+
+ if(id_bs->proto_id != id_bt->proto_id){
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "check and compare ids : proto_id mismatch %d != %d\n",
+ id_bs->proto_id, id_bt->proto_id);
+
+ return 1;
+ }
+
+ /* compare the ID data. */
+
+ switch (id_bt->type) {
+ case IPSECDOI_ID_DER_ASN1_DN:
+ case IPSECDOI_ID_DER_ASN1_GN:
+ /* compare asn1 ids */
+ result = eay_cmp_asn1dn(&ident_t, &ident_s);
+ goto cmpid_result;
+
+ case IPSECDOI_ID_IPV4_ADDR:
+ /* validate lengths */
+ if ((ident_t.l != sizeof(struct in_addr))||
+ (ident_s.l != sizeof(struct in_addr)))
+ goto cmpid_invalid;
+ break;
+
+ case IPSECDOI_ID_IPV4_ADDR_SUBNET:
+ case IPSECDOI_ID_IPV4_ADDR_RANGE:
+ /* validate lengths */
+ if ((ident_t.l != (sizeof(struct in_addr)*2))||
+ (ident_s.l != (sizeof(struct in_addr)*2)))
+ goto cmpid_invalid;
+ break;
+
+#ifdef INET6
+ case IPSECDOI_ID_IPV6_ADDR:
+ /* validate lengths */
+ if ((ident_t.l != sizeof(struct in6_addr))||
+ (ident_s.l != sizeof(struct in6_addr)))
+ goto cmpid_invalid;
+ break;
+
+ case IPSECDOI_ID_IPV6_ADDR_SUBNET:
+ case IPSECDOI_ID_IPV6_ADDR_RANGE:
+ /* validate lengths */
+ if ((ident_t.l != (sizeof(struct in6_addr)*2))||
+ (ident_s.l != (sizeof(struct in6_addr)*2)))
+ goto cmpid_invalid;
+ break;
+#endif
+ case IPSECDOI_ID_FQDN:
+ case IPSECDOI_ID_USER_FQDN:
+ case IPSECDOI_ID_KEY_ID:
+ break;
+
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Unhandled id type %i specified for comparison\n",
+ id_bt->type);
+ return -1;
+ }
+
+ /* validate matching data and length */
+ if (ident_t.l == ident_s.l)
+ result = memcmp(ident_t.v,ident_s.v,ident_t.l);
+ else
+ result = 1;
+
+cmpid_result:
+
+ /* debug level output */
+ if(loglevel >= LLV_DEBUG) {
+ char *idstrt = ipsecdoi_id2str(idt);
+ char *idstrs = ipsecdoi_id2str(ids);
+
+ if (!result)
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "check and compare ids : values matched (%s)\n",
+ s_ipsecdoi_ident(id_bs->type) );
+ else
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "check and compare ids : value mismatch (%s)\n",
+ s_ipsecdoi_ident(id_bs->type));
+
+ plog(LLV_DEBUG, LOCATION, NULL, "cmpid target: \'%s\'\n", idstrt );
+ plog(LLV_DEBUG, LOCATION, NULL, "cmpid source: \'%s\'\n", idstrs );
+
+ racoon_free(idstrs);
+ racoon_free(idstrt);
+ }
+
+ /* return result */
+ if( !result )
+ return 0;
+ else
+ return 1;
+
+cmpid_invalid:
+
+ /* id integrity error */
+ plog(LLV_DEBUG, LOCATION, NULL, "check and compare ids : %s integrity error\n",
+ s_ipsecdoi_ident(id_bs->type));
+ plog(LLV_DEBUG, LOCATION, NULL, "cmpid target: length = \'%zu\'\n", ident_t.l );
+ plog(LLV_DEBUG, LOCATION, NULL, "cmpid source: length = \'%zu\'\n", ident_s.l );
+
+ return -1;
+}
+
+/*
+ * check the following:
+ * - In main mode with pre-shared key, only address type can be used.
+ * - if proper type for phase 1 ?
+ * - if phase 1 ID payload conformed RFC2407 4.6.2.
+ * (proto, port) must be (0, 0), (udp, 500) or (udp, [specified]).
+ * - if ID payload sent from peer is equal to the ID expected by me.
+ *
+ * both of "id" and "id_p" should be ID payload without general header,
+ */
+int
+ipsecdoi_checkid1(iph1)
+ struct ph1handle *iph1;
+{
+ struct ipsecdoi_id_b *id_b;
+
+ if (iph1->id_p == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid iph1 passed id_p == NULL\n");
+ return ISAKMP_INTERNAL_ERROR;
+ }
+ if (iph1->id_p->l < sizeof(*id_b)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid value passed as \"ident\" (len=%lu)\n",
+ (u_long)iph1->id_p->l);
+ return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
+ }
+
+ id_b = (struct ipsecdoi_id_b *)iph1->id_p->v;
+
+ /* In main mode with pre-shared key, only address type can be used. */
+ if (iph1->etype == ISAKMP_ETYPE_IDENT &&
+ iph1->approval->authmethod == OAKLEY_ATTR_AUTH_METHOD_PSKEY) {
+ if (id_b->type != IPSECDOI_ID_IPV4_ADDR
+ && id_b->type != IPSECDOI_ID_IPV6_ADDR) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Expecting IP address type in main mode, "
+ "but %s.\n", s_ipsecdoi_ident(id_b->type));
+ return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
+ }
+ }
+
+ /* if proper type for phase 1 ? */
+ switch (id_b->type) {
+ case IPSECDOI_ID_IPV4_ADDR_SUBNET:
+ case IPSECDOI_ID_IPV6_ADDR_SUBNET:
+ case IPSECDOI_ID_IPV4_ADDR_RANGE:
+ case IPSECDOI_ID_IPV6_ADDR_RANGE:
+ plog(LLV_WARNING, LOCATION, NULL,
+ "such ID type %s is not proper.\n",
+ s_ipsecdoi_ident(id_b->type));
+ /*FALLTHROUGH*/
+ }
+
+ /* if phase 1 ID payload conformed RFC2407 4.6.2. */
+ if (id_b->type == IPSECDOI_ID_IPV4_ADDR ||
+ id_b->type == IPSECDOI_ID_IPV6_ADDR) {
+
+ if (id_b->proto_id == 0 && ntohs(id_b->port) != 0) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "protocol ID and Port mismatched. "
+ "proto_id:%d port:%d\n",
+ id_b->proto_id, ntohs(id_b->port));
+ /*FALLTHROUGH*/
+
+ } else if (id_b->proto_id == IPPROTO_UDP) {
+ /*
+ * copmaring with expecting port.
+ * always permit if port is equal to PORT_ISAKMP
+ */
+ if (ntohs(id_b->port) != PORT_ISAKMP) {
+ u_int16_t port;
+
+ port = extract_port(iph1->remote);
+ if (ntohs(id_b->port) != port) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "port %d expected, but %d\n",
+ port, ntohs(id_b->port));
+ /*FALLTHROUGH*/
+ }
+ }
+ }
+ }
+
+ /* resolve remote configuration if not done yet */
+ if (resolveph1rmconf(iph1) < 0)
+ return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
+
+ if (iph1->rmconf == NULL)
+ return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
+
+ return 0;
+}
+
+/*
+ * create ID payload for phase 1 and set into iph1->id.
+ * NOT INCLUDING isakmp general header.
+ * see, RFC2407 4.6.2.1
+ */
+int
+ipsecdoi_setid1(iph1)
+ struct ph1handle *iph1;
+{
+ vchar_t *ret = NULL;
+ struct ipsecdoi_id_b id_b;
+ vchar_t *ident = NULL;
+ struct sockaddr *ipid = NULL;
+
+ /* init */
+ id_b.proto_id = 0;
+ id_b.port = 0;
+ ident = NULL;
+
+ switch (iph1->rmconf->idvtype) {
+ case IDTYPE_FQDN:
+ id_b.type = IPSECDOI_ID_FQDN;
+ ident = vdup(iph1->rmconf->idv);
+ break;
+ case IDTYPE_USERFQDN:
+ id_b.type = IPSECDOI_ID_USER_FQDN;
+ ident = vdup(iph1->rmconf->idv);
+ break;
+ case IDTYPE_KEYID:
+ id_b.type = IPSECDOI_ID_KEY_ID;
+ ident = vdup(iph1->rmconf->idv);
+ break;
+ case IDTYPE_ASN1DN:
+ id_b.type = IPSECDOI_ID_DER_ASN1_DN;
+ if (iph1->rmconf->idv) {
+ /* XXX it must be encoded to asn1dn. */
+ ident = vdup(iph1->rmconf->idv);
+ } else {
+ if (oakley_getmycert(iph1) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get own CERT.\n");
+ goto err;
+ }
+ ident = eay_get_x509asn1subjectname(iph1->cert);
+ }
+ break;
+ case IDTYPE_ADDRESS:
+ /*
+ * if the value of the id type was set by the configuration
+ * file, then use it. otherwise the value is get from local
+ * ip address by using ike negotiation.
+ */
+ if (iph1->rmconf->idv)
+ ipid = (struct sockaddr *)iph1->rmconf->idv->v;
+ /*FALLTHROUGH*/
+ default:
+ {
+ int l;
+ caddr_t p;
+
+ if (ipid == NULL)
+ ipid = iph1->local;
+
+ /* use IP address */
+ switch (ipid->sa_family) {
+ case AF_INET:
+ id_b.type = IPSECDOI_ID_IPV4_ADDR;
+ l = sizeof(struct in_addr);
+ p = (caddr_t)&((struct sockaddr_in *)ipid)->sin_addr;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ id_b.type = IPSECDOI_ID_IPV6_ADDR;
+ l = sizeof(struct in6_addr);
+ p = (caddr_t)&((struct sockaddr_in6 *)ipid)->sin6_addr;
+ break;
+#endif
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid address family.\n");
+ goto err;
+ }
+ id_b.proto_id = IPPROTO_UDP;
+ id_b.port = htons(PORT_ISAKMP);
+ ident = vmalloc(l);
+ if (!ident) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get ID buffer.\n");
+ return -1;
+ }
+ memcpy(ident->v, p, ident->l);
+ }
+ }
+ if (!ident) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get ID buffer.\n");
+ return -1;
+ }
+
+ ret = vmalloc(sizeof(id_b) + ident->l);
+ if (ret == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get ID buffer.\n");
+ goto err;
+ }
+
+ memcpy(ret->v, &id_b, sizeof(id_b));
+ memcpy(ret->v + sizeof(id_b), ident->v, ident->l);
+
+ iph1->id = ret;
+
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "use ID type of %s\n", s_ipsecdoi_ident(id_b.type));
+ if (ident)
+ vfree(ident);
+ return 0;
+
+err:
+ if (ident)
+ vfree(ident);
+ plog(LLV_ERROR, LOCATION, NULL, "failed get my ID\n");
+ return -1;
+}
+
+/* it's only called by cfparse.y. */
+int
+set_identifier(vpp, type, value)
+ vchar_t **vpp, *value;
+ int type;
+{
+ return set_identifier_qual(vpp, type, value, IDQUAL_UNSPEC);
+}
+
+int
+set_identifier_qual(vpp, type, value, qual)
+ vchar_t **vpp, *value;
+ int type;
+ int qual;
+{
+ vchar_t *new = NULL;
+
+ /* simply return if value is null. */
+ if (!value){
+ if( type == IDTYPE_FQDN || type == IDTYPE_USERFQDN){
+ plog(LLV_ERROR, LOCATION, NULL,
+ "No %s\n", type == IDTYPE_FQDN ? "fqdn":"user fqdn");
+ return -1;
+ }
+ return 0;
+ }
+
+ switch (type) {
+ case IDTYPE_FQDN:
+ case IDTYPE_USERFQDN:
+ if(value->l <= 1){
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Empty %s\n", type == IDTYPE_FQDN ? "fqdn":"user fqdn");
+ return -1;
+ }
+ /* length is adjusted since QUOTEDSTRING teminates NULL. */
+ new = vmalloc(value->l - 1);
+ if (new == NULL)
+ return -1;
+ memcpy(new->v, value->v, new->l);
+ break;
+ case IDTYPE_KEYID:
+ /*
+ * If no qualifier is specified: IDQUAL_UNSPEC. It means
+ * to use a file for backward compatibility sake.
+ */
+ switch(qual) {
+ case IDQUAL_FILE:
+ case IDQUAL_UNSPEC: {
+ FILE *fp;
+ char b[512];
+ int tlen, len;
+
+ fp = fopen(value->v, "r");
+ if (fp == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "can not open %s\n", value->v);
+ return -1;
+ }
+ tlen = 0;
+ while ((len = fread(b, 1, sizeof(b), fp)) != 0) {
+ new = vrealloc(new, tlen + len);
+ if (!new) {
+ fclose(fp);
+ return -1;
+ }
+ memcpy(new->v + tlen, b, len);
+ tlen += len;
+ }
+ fclose(fp);
+ break;
+ }
+
+ case IDQUAL_TAG:
+ new = vmalloc(value->l - 1);
+ if (new == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "can not allocate memory");
+ return -1;
+ }
+ memcpy(new->v, value->v, new->l);
+ break;
+
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "unknown qualifier");
+ return -1;
+ }
+ break;
+
+ case IDTYPE_ADDRESS: {
+ struct sockaddr *sa;
+
+ /* length is adjusted since QUOTEDSTRING teminates NULL. */
+ if (value->l == 0)
+ break;
+
+ sa = str2saddr(value->v, NULL);
+ if (sa == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid ip address %s\n", value->v);
+ return -1;
+ }
+
+ new = vmalloc(sysdep_sa_len(sa));
+ if (new == NULL) {
+ racoon_free(sa);
+ return -1;
+ }
+ memcpy(new->v, sa, new->l);
+ racoon_free(sa);
+ break;
+ }
+ case IDTYPE_ASN1DN:
+ if (value->v[0] == '~')
+ /* Hex-encoded ASN1 strings */
+ new = eay_hex2asn1dn(value->v + 1, - 1);
+ else
+ /* DN encoded strings */
+ new = eay_str2asn1dn(value->v, value->l - 1);
+
+ if (new == NULL)
+ return -1;
+
+ if (loglevel >= LLV_DEBUG) {
+ X509_NAME *xn;
+ BIO *bio;
+ unsigned char *ptr = (unsigned char *) new->v, *buf;
+ size_t len;
+ char save;
+
+ xn = d2i_X509_NAME(NULL, (void *)&ptr, new->l);
+ bio = BIO_new(BIO_s_mem());
+
+ X509_NAME_print_ex(bio, xn, 0, 0);
+ len = BIO_get_mem_data(bio, &ptr);
+ save = ptr[len];
+ ptr[len] = 0;
+ plog(LLV_DEBUG, LOCATION, NULL, "Parsed DN: %s\n", ptr);
+ ptr[len] = save;
+ X509_NAME_free(xn);
+ BIO_free(bio);
+ }
+
+ break;
+ }
+
+ *vpp = new;
+
+ return 0;
+}
+
+/*
+ * create ID payload for phase 2, and set into iph2->id and id_p. There are
+ * NOT INCLUDING isakmp general header.
+ * this function is for initiator. responder will get to copy from payload.
+ * responder ID type is always address type.
+ * see, RFC2407 4.6.2.1
+ */
+int
+ipsecdoi_setid2(iph2)
+ struct ph2handle *iph2;
+{
+ struct secpolicy *sp;
+
+ /* check there is phase 2 handler ? */
+ sp = getspbyspid(iph2->spid);
+ if (sp == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "no policy found for spid:%u.\n", iph2->spid);
+ return -1;
+ }
+
+ if (!ipsecdoi_transportmode(iph2->proposal))
+ iph2->id = ipsecdoi_sockaddr2id((struct sockaddr *)&sp->spidx.src,
+ sp->spidx.prefs, sp->spidx.ul_proto);
+ else if (iph2->sa_src != NULL) {
+ /* He have a specific hint indicating that the transport
+ * mode SA will be negotiated using addresses that differ
+ * with the one from the SA. We need to indicate that to
+ * our peer by setting the SA address as ID.
+ * This is typically the case for the bootstrapping of the
+ * transport mode SA protecting BU/BA for MIPv6 traffic
+ *
+ * --arno*/
+ iph2->id = ipsecdoi_sockaddr2id(iph2->sa_src,
+ IPSECDOI_PREFIX_HOST,
+ sp->spidx.ul_proto);
+ } else
+ iph2->id = ipsecdoi_sockaddr2id(iph2->src, IPSECDOI_PREFIX_HOST,
+ sp->spidx.ul_proto);
+
+ if (iph2->id == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get ID for %s\n",
+ spidx2str(&sp->spidx));
+ return -1;
+ }
+ plog(LLV_DEBUG, LOCATION, NULL, "use local ID type %s\n",
+ s_ipsecdoi_ident(((struct ipsecdoi_id_b *)iph2->id->v)->type));
+
+ /* remote side */
+ if (!ipsecdoi_transportmode(iph2->proposal))
+ iph2->id_p = ipsecdoi_sockaddr2id((struct sockaddr *)&sp->spidx.dst,
+ sp->spidx.prefd, sp->spidx.ul_proto);
+ else if (iph2->sa_dst != NULL) {
+ /* See comment above for local side. */
+ iph2->id_p = ipsecdoi_sockaddr2id(iph2->sa_dst,
+ IPSECDOI_PREFIX_HOST,
+ sp->spidx.ul_proto);
+ } else
+ iph2->id_p = ipsecdoi_sockaddr2id(iph2->dst, IPSECDOI_PREFIX_HOST,
+ sp->spidx.ul_proto);
+
+ if (iph2->id_p == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get ID for %s\n",
+ spidx2str(&sp->spidx));
+ VPTRINIT(iph2->id);
+ return -1;
+ }
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "use remote ID type %s\n",
+ s_ipsecdoi_ident(((struct ipsecdoi_id_b *)iph2->id_p->v)->type));
+
+ return 0;
+}
+
+/*
+ * set address type of ID.
+ * NOT INCLUDING general header.
+ */
+vchar_t *
+ipsecdoi_sockaddr2id(saddr, prefixlen, ul_proto)
+ struct sockaddr *saddr;
+ u_int prefixlen;
+ u_int ul_proto;
+{
+ vchar_t *new;
+ int type, len1, len2;
+ caddr_t sa;
+ u_short port;
+
+ /*
+ * Q. When type is SUBNET, is it allowed to be ::1/128.
+ * A. Yes. (consensus at bake-off)
+ */
+ switch (saddr->sa_family) {
+ case AF_INET:
+ len1 = sizeof(struct in_addr);
+ if (prefixlen >= (sizeof(struct in_addr) << 3)) {
+ type = IPSECDOI_ID_IPV4_ADDR;
+ len2 = 0;
+ } else {
+ type = IPSECDOI_ID_IPV4_ADDR_SUBNET;
+ len2 = sizeof(struct in_addr);
+ }
+ sa = (caddr_t)&((struct sockaddr_in *)(saddr))->sin_addr;
+ port = ((struct sockaddr_in *)(saddr))->sin_port;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ len1 = sizeof(struct in6_addr);
+ if (prefixlen >= (sizeof(struct in6_addr) << 3)) {
+ type = IPSECDOI_ID_IPV6_ADDR;
+ len2 = 0;
+ } else {
+ type = IPSECDOI_ID_IPV6_ADDR_SUBNET;
+ len2 = sizeof(struct in6_addr);
+ }
+ sa = (caddr_t)&((struct sockaddr_in6 *)(saddr))->sin6_addr;
+ port = ((struct sockaddr_in6 *)(saddr))->sin6_port;
+ break;
+#endif
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid family: %d.\n", saddr->sa_family);
+ return NULL;
+ }
+
+ /* get ID buffer */
+ new = vmalloc(sizeof(struct ipsecdoi_id_b) + len1 + len2);
+ if (new == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get ID buffer.\n");
+ return NULL;
+ }
+
+ memset(new->v, 0, new->l);
+
+ /* set the part of header. */
+ ((struct ipsecdoi_id_b *)new->v)->type = type;
+
+ /* set ul_proto and port */
+ /*
+ * NOTE: we use both IPSEC_ULPROTO_ANY and IPSEC_PORT_ANY as wild card
+ * because 0 means port number of 0. Instead of 0, we use IPSEC_*_ANY.
+ */
+ ((struct ipsecdoi_id_b *)new->v)->proto_id =
+ ul_proto == IPSEC_ULPROTO_ANY ? 0 : ul_proto;
+ ((struct ipsecdoi_id_b *)new->v)->port =
+ port == IPSEC_PORT_ANY ? 0 : port;
+ memcpy(new->v + sizeof(struct ipsecdoi_id_b), sa, len1);
+
+ /* set address */
+
+ /* set prefix */
+ if (len2) {
+ u_char *p = (unsigned char *) new->v +
+ sizeof(struct ipsecdoi_id_b) + len1;
+ u_int bits = prefixlen;
+
+ while (bits >= 8) {
+ *p++ = 0xff;
+ bits -= 8;
+ }
+
+ if (bits > 0)
+ *p = ~((1 << (8 - bits)) - 1);
+ }
+
+ return new;
+}
+
+vchar_t *
+ipsecdoi_sockrange2id(laddr, haddr, ul_proto)
+ struct sockaddr *laddr, *haddr;
+ u_int ul_proto;
+{
+ vchar_t *new;
+ int type, len1, len2;
+ u_short port;
+
+ if (laddr->sa_family != haddr->sa_family) {
+ plog(LLV_ERROR, LOCATION, NULL, "Address family mismatch\n");
+ return NULL;
+ }
+
+ switch (laddr->sa_family) {
+ case AF_INET:
+ type = IPSECDOI_ID_IPV4_ADDR_RANGE;
+ len1 = sizeof(struct in_addr);
+ len2 = sizeof(struct in_addr);
+ break;
+#ifdef INET6
+ case AF_INET6:
+ type = IPSECDOI_ID_IPV6_ADDR_RANGE;
+ len1 = sizeof(struct in6_addr);
+ len2 = sizeof(struct in6_addr);
+ break;
+#endif
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid family: %d.\n", laddr->sa_family);
+ return NULL;
+ }
+
+ /* get ID buffer */
+ new = vmalloc(sizeof(struct ipsecdoi_id_b) + len1 + len2);
+ if (new == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get ID buffer.\n");
+ return NULL;
+ }
+
+ memset(new->v, 0, new->l);
+ /* set the part of header. */
+ ((struct ipsecdoi_id_b *)new->v)->type = type;
+
+ /* set ul_proto and port */
+ /*
+ * NOTE: we use both IPSEC_ULPROTO_ANY and IPSEC_PORT_ANY as wild card
+ * because 0 means port number of 0. Instead of 0, we use IPSEC_*_ANY.
+ */
+ ((struct ipsecdoi_id_b *)new->v)->proto_id =
+ ul_proto == IPSEC_ULPROTO_ANY ? 0 : ul_proto;
+ port = ((struct sockaddr_in *)(laddr))->sin_port;
+ ((struct ipsecdoi_id_b *)new->v)->port =
+ port == IPSEC_PORT_ANY ? 0 : port;
+ memcpy(new->v + sizeof(struct ipsecdoi_id_b),
+ (caddr_t)&((struct sockaddr_in *)(laddr))->sin_addr,
+ len1);
+ memcpy(new->v + sizeof(struct ipsecdoi_id_b) + len1,
+ (caddr_t)&((struct sockaddr_in *)haddr)->sin_addr,
+ len2);
+ return new;
+}
+
+
+/*
+ * create sockaddr structure from ID payload (buf).
+ * buffers (saddr, prefixlen, ul_proto) must be allocated.
+ * see, RFC2407 4.6.2.1
+ */
+int
+ipsecdoi_id2sockaddr(buf, saddr, prefixlen, ul_proto)
+ vchar_t *buf;
+ struct sockaddr *saddr;
+ u_int8_t *prefixlen;
+ u_int16_t *ul_proto;
+{
+ struct ipsecdoi_id_b *id_b = NULL;
+ u_int plen = 0;
+
+ if (buf == NULL)
+ return ISAKMP_INTERNAL_ERROR;
+
+ id_b = (struct ipsecdoi_id_b *)buf->v;
+
+ /*
+ * When a ID payload of subnet type with a IP address of full bit
+ * masked, it has to be processed as host address.
+ * e.g. below 2 type are same.
+ * type = ipv6 subnet, data = 2001::1/128
+ * type = ipv6 address, data = 2001::1
+ */
+ switch (id_b->type) {
+ case IPSECDOI_ID_IPV4_ADDR:
+ case IPSECDOI_ID_IPV4_ADDR_SUBNET:
+#ifndef __linux__
+ saddr->sa_len = sizeof(struct sockaddr_in);
+#endif
+ saddr->sa_family = AF_INET;
+ ((struct sockaddr_in *)saddr)->sin_port =
+ (id_b->port == 0
+ ? IPSEC_PORT_ANY
+ : id_b->port); /* see sockaddr2id() */
+ memcpy(&((struct sockaddr_in *)saddr)->sin_addr,
+ buf->v + sizeof(*id_b), sizeof(struct in_addr));
+ break;
+#ifdef INET6
+ case IPSECDOI_ID_IPV6_ADDR:
+ case IPSECDOI_ID_IPV6_ADDR_SUBNET:
+#ifndef __linux__
+ saddr->sa_len = sizeof(struct sockaddr_in6);
+#endif
+ saddr->sa_family = AF_INET6;
+ ((struct sockaddr_in6 *)saddr)->sin6_port =
+ (id_b->port == 0
+ ? IPSEC_PORT_ANY
+ : id_b->port); /* see sockaddr2id() */
+ memcpy(&((struct sockaddr_in6 *)saddr)->sin6_addr,
+ buf->v + sizeof(*id_b), sizeof(struct in6_addr));
+ ((struct sockaddr_in6 *)saddr)->sin6_scope_id =
+ (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)saddr)->sin6_addr)
+ ? ((struct sockaddr_in6 *)id_b)->sin6_scope_id
+ : 0);
+
+ break;
+#endif
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "unsupported ID type %d\n", id_b->type);
+ return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
+ }
+
+ /* get prefix length */
+ switch (id_b->type) {
+ case IPSECDOI_ID_IPV4_ADDR:
+ plen = sizeof(struct in_addr) << 3;
+ break;
+#ifdef INET6
+ case IPSECDOI_ID_IPV6_ADDR:
+ plen = sizeof(struct in6_addr) << 3;
+ break;
+#endif
+ case IPSECDOI_ID_IPV4_ADDR_SUBNET:
+#ifdef INET6
+ case IPSECDOI_ID_IPV6_ADDR_SUBNET:
+#endif
+ {
+ u_char *p;
+ u_int max;
+ int alen = sizeof(struct in_addr);
+
+ switch (id_b->type) {
+ case IPSECDOI_ID_IPV4_ADDR_SUBNET:
+ alen = sizeof(struct in_addr);
+ break;
+#ifdef INET6
+ case IPSECDOI_ID_IPV6_ADDR_SUBNET:
+ alen = sizeof(struct in6_addr);
+ break;
+#endif
+ }
+
+ /* sanity check */
+ if (buf->l < alen)
+ return ISAKMP_INTERNAL_ERROR;
+
+ /* get subnet mask length */
+ plen = 0;
+ max = alen <<3;
+
+ p = (unsigned char *) buf->v
+ + sizeof(struct ipsecdoi_id_b)
+ + alen;
+
+ for (; *p == 0xff; p++) {
+ plen += 8;
+ if (plen >= max)
+ break;
+ }
+
+ if (plen < max) {
+ u_int l = 0;
+ u_char b = ~(*p);
+
+ while (b) {
+ b >>= 1;
+ l++;
+ }
+
+ l = 8 - l;
+ plen += l;
+ }
+ }
+ break;
+ }
+
+ *prefixlen = plen;
+ *ul_proto = id_b->proto_id == 0
+ ? IPSEC_ULPROTO_ANY
+ : id_b->proto_id; /* see sockaddr2id() */
+
+ return 0;
+}
+
+/*
+ * make printable string from ID payload except of general header.
+ */
+char *
+ipsecdoi_id2str(id)
+ const vchar_t *id;
+{
+#define BUFLEN 512
+ char * ret = NULL;
+ int len = 0;
+ char *dat;
+ static char buf[BUFLEN];
+ struct ipsecdoi_id_b *id_b = (struct ipsecdoi_id_b *)id->v;
+ union sockaddr_any saddr;
+ u_int plen = 0;
+
+ switch (id_b->type) {
+ case IPSECDOI_ID_IPV4_ADDR:
+ case IPSECDOI_ID_IPV4_ADDR_SUBNET:
+ case IPSECDOI_ID_IPV4_ADDR_RANGE:
+
+#ifndef __linux__
+ saddr.sa.sa_len = sizeof(struct sockaddr_in);
+#endif
+ saddr.sa.sa_family = AF_INET;
+ saddr.sin.sin_port = IPSEC_PORT_ANY;
+ memcpy(&saddr.sin.sin_addr,
+ id->v + sizeof(*id_b), sizeof(struct in_addr));
+ break;
+#ifdef INET6
+ case IPSECDOI_ID_IPV6_ADDR:
+ case IPSECDOI_ID_IPV6_ADDR_SUBNET:
+ case IPSECDOI_ID_IPV6_ADDR_RANGE:
+
+#ifndef __linux__
+ saddr.sa.sa_len = sizeof(struct sockaddr_in6);
+#endif
+ saddr.sa.sa_family = AF_INET6;
+ saddr.sin6.sin6_port = IPSEC_PORT_ANY;
+ memcpy(&saddr.sin6.sin6_addr,
+ id->v + sizeof(*id_b), sizeof(struct in6_addr));
+ saddr.sin6.sin6_scope_id =
+ (IN6_IS_ADDR_LINKLOCAL(&saddr.sin6.sin6_addr)
+ ? ((struct sockaddr_in6 *)id_b)->sin6_scope_id
+ : 0);
+ break;
+#endif
+ }
+
+ switch (id_b->type) {
+ case IPSECDOI_ID_IPV4_ADDR:
+#ifdef INET6
+ case IPSECDOI_ID_IPV6_ADDR:
+#endif
+ len = snprintf( buf, BUFLEN, "%s", saddrwop2str(&saddr.sa));
+ break;
+
+ case IPSECDOI_ID_IPV4_ADDR_SUBNET:
+#ifdef INET6
+ case IPSECDOI_ID_IPV6_ADDR_SUBNET:
+#endif
+ {
+ u_char *p;
+ u_int max;
+ int alen = sizeof(struct in_addr);
+
+ switch (id_b->type) {
+ case IPSECDOI_ID_IPV4_ADDR_SUBNET:
+ alen = sizeof(struct in_addr);
+ break;
+#ifdef INET6
+ case IPSECDOI_ID_IPV6_ADDR_SUBNET:
+ alen = sizeof(struct in6_addr);
+ break;
+#endif
+ }
+
+ /* sanity check */
+ if (id->l < alen) {
+ len = 0;
+ break;
+ }
+
+ /* get subnet mask length */
+ plen = 0;
+ max = alen <<3;
+
+ p = (unsigned char *) id->v
+ + sizeof(struct ipsecdoi_id_b)
+ + alen;
+
+ for (; *p == 0xff; p++) {
+ plen += 8;
+ if (plen >= max)
+ break;
+ }
+
+ if (plen < max) {
+ u_int l = 0;
+ u_char b = ~(*p);
+
+ while (b) {
+ b >>= 1;
+ l++;
+ }
+
+ l = 8 - l;
+ plen += l;
+ }
+
+ len = snprintf( buf, BUFLEN, "%s/%i", saddrwop2str(&saddr.sa), plen);
+ }
+ break;
+
+ case IPSECDOI_ID_IPV4_ADDR_RANGE:
+
+ len = snprintf( buf, BUFLEN, "%s-", saddrwop2str(&saddr.sa));
+
+#ifndef __linux__
+ saddr.sa.sa_len = sizeof(struct sockaddr_in);
+#endif
+ saddr.sa.sa_family = AF_INET;
+ saddr.sin.sin_port = IPSEC_PORT_ANY;
+ memcpy(&saddr.sin.sin_addr,
+ id->v + sizeof(*id_b) + sizeof(struct in_addr),
+ sizeof(struct in_addr));
+
+ len += snprintf(buf + len, BUFLEN - len, "%s", saddrwop2str(&saddr.sa));
+ break;
+
+#ifdef INET6
+ case IPSECDOI_ID_IPV6_ADDR_RANGE:
+ len = snprintf( buf, BUFLEN, "%s-", saddrwop2str(&saddr.sa));
+
+#ifndef __linux__
+ saddr.sa.sa_len = sizeof(struct sockaddr_in6);
+#endif
+ saddr.sa.sa_family = AF_INET6;
+ saddr.sin6.sin6_port = IPSEC_PORT_ANY;
+ memcpy(&saddr.sin6.sin6_addr,
+ id->v + sizeof(*id_b) + sizeof(struct in6_addr),
+ sizeof(struct in6_addr));
+ saddr.sin6.sin6_scope_id =
+ (IN6_IS_ADDR_LINKLOCAL(&saddr.sin6.sin6_addr)
+ ? ((struct sockaddr_in6 *)id_b)->sin6_scope_id
+ : 0);
+
+ len += snprintf(buf + len, BUFLEN - len, "%s", saddrwop2str(&saddr.sa));
+ break;
+#endif
+
+ case IPSECDOI_ID_FQDN:
+ case IPSECDOI_ID_USER_FQDN:
+ len = id->l - sizeof(*id_b);
+ if (len > BUFLEN)
+ len = BUFLEN;
+ memcpy(buf, id->v + sizeof(*id_b), len);
+ break;
+
+ case IPSECDOI_ID_DER_ASN1_DN:
+ case IPSECDOI_ID_DER_ASN1_GN:
+ {
+ X509_NAME *xn = NULL;
+
+ dat = id->v + sizeof(*id_b);
+ len = id->l - sizeof(*id_b);
+
+ if (d2i_X509_NAME(&xn, (void*) &dat, len) != NULL) {
+ BIO *bio = BIO_new(BIO_s_mem());
+ X509_NAME_print_ex(bio, xn, 0, 0);
+ len = BIO_get_mem_data(bio, &dat);
+ if (len > BUFLEN)
+ len = BUFLEN;
+ memcpy(buf,dat,len);
+ BIO_free(bio);
+ X509_NAME_free(xn);
+ } else {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "unable to extract asn1dn from id\n");
+
+ len = sprintf(buf, "<ASN1-DN>");
+ }
+
+ break;
+ }
+
+ /* currently unhandled id types */
+ case IPSECDOI_ID_KEY_ID:
+ len = sprintf( buf, "<KEY-ID>");
+ break;
+
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "unknown ID type %d\n", id_b->type);
+ }
+
+ if (!len)
+ len = sprintf( buf, "<?>");
+
+ ret = racoon_malloc(len+1);
+ if (ret != NULL) {
+ memcpy(ret,buf,len);
+ ret[len]=0;
+ }
+
+ return ret;
+}
+
+/*
+ * set IPsec data attributes into a proposal.
+ * NOTE: MUST called per a transform.
+ */
+int
+ipsecdoi_t2satrns(t, pp, pr, tr)
+ struct isakmp_pl_t *t;
+ struct saprop *pp;
+ struct saproto *pr;
+ struct satrns *tr;
+{
+ struct isakmp_data *d, *prev;
+ int flag, type;
+ int error = -1;
+ int life_t;
+ int tlen;
+
+ tr->trns_no = t->t_no;
+ tr->trns_id = t->t_id;
+
+ tlen = ntohs(t->h.len) - sizeof(*t);
+ prev = (struct isakmp_data *)NULL;
+ d = (struct isakmp_data *)(t + 1);
+
+ /* default */
+ life_t = IPSECDOI_ATTR_SA_LD_TYPE_DEFAULT;
+ pp->lifetime = IPSECDOI_ATTR_SA_LD_SEC_DEFAULT;
+ pp->lifebyte = 0;
+ tr->authtype = IPSECDOI_ATTR_AUTH_NONE;
+
+ while (tlen > 0) {
+
+ type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
+ flag = ntohs(d->type) & ISAKMP_GEN_MASK;
+
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "type=%s, flag=0x%04x, lorv=%s\n",
+ s_ipsecdoi_attr(type), flag,
+ s_ipsecdoi_attr_v(type, ntohs(d->lorv)));
+
+ switch (type) {
+ case IPSECDOI_ATTR_SA_LD_TYPE:
+ {
+ int type = ntohs(d->lorv);
+ switch (type) {
+ case IPSECDOI_ATTR_SA_LD_TYPE_SEC:
+ case IPSECDOI_ATTR_SA_LD_TYPE_KB:
+ life_t = type;
+ break;
+ default:
+ plog(LLV_WARNING, LOCATION, NULL,
+ "invalid life duration type. "
+ "use default\n");
+ life_t = IPSECDOI_ATTR_SA_LD_TYPE_DEFAULT;
+ break;
+ }
+ break;
+ }
+ case IPSECDOI_ATTR_SA_LD:
+ if (prev == NULL
+ || (ntohs(prev->type) & ~ISAKMP_GEN_MASK) !=
+ IPSECDOI_ATTR_SA_LD_TYPE) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "life duration must follow ltype\n");
+ break;
+ }
+
+ {
+ u_int32_t t;
+ vchar_t *ld_buf = NULL;
+
+ if (flag) {
+ /* i.e. ISAKMP_GEN_TV */
+ ld_buf = vmalloc(sizeof(d->lorv));
+ if (ld_buf == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get LD buffer.\n");
+ goto end;
+ }
+ memcpy(ld_buf->v, &d->lorv, sizeof(d->lorv));
+ } else {
+ int len = ntohs(d->lorv);
+ /* i.e. ISAKMP_GEN_TLV */
+ ld_buf = vmalloc(len);
+ if (ld_buf == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get LD buffer.\n");
+ goto end;
+ }
+ memcpy(ld_buf->v, d + 1, len);
+ }
+ switch (life_t) {
+ case IPSECDOI_ATTR_SA_LD_TYPE_SEC:
+ t = ipsecdoi_set_ld(ld_buf);
+ vfree(ld_buf);
+ if (t == 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid life duration.\n");
+ goto end;
+ }
+ /* lifetime must be equal in a proposal. */
+ if (pp->lifetime == IPSECDOI_ATTR_SA_LD_SEC_DEFAULT)
+ pp->lifetime = t;
+ else if (pp->lifetime != t) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "lifetime mismatched "
+ "in a proposal, "
+ "prev:%ld curr:%u.\n",
+ (long)pp->lifetime, t);
+ goto end;
+ }
+ break;
+ case IPSECDOI_ATTR_SA_LD_TYPE_KB:
+ t = ipsecdoi_set_ld(ld_buf);
+ vfree(ld_buf);
+ if (t == 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid life duration.\n");
+ goto end;
+ }
+ /* lifebyte must be equal in a proposal. */
+ if (pp->lifebyte == 0)
+ pp->lifebyte = t;
+ else if (pp->lifebyte != t) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "lifebyte mismatched "
+ "in a proposal, "
+ "prev:%d curr:%u.\n",
+ pp->lifebyte, t);
+ goto end;
+ }
+ break;
+ default:
+ vfree(ld_buf);
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid life type: %d\n", life_t);
+ goto end;
+ }
+ }
+ break;
+
+ case IPSECDOI_ATTR_GRP_DESC:
+ /*
+ * RFC2407: 4.5 IPSEC Security Association Attributes
+ * Specifies the Oakley Group to be used in a PFS QM
+ * negotiation. For a list of supported values, see
+ * Appendix A of [IKE].
+ */
+ if (pp->pfs_group == 0)
+ pp->pfs_group = (u_int16_t)ntohs(d->lorv);
+ else if (pp->pfs_group != (u_int16_t)ntohs(d->lorv)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "pfs_group mismatched "
+ "in a proposal.\n");
+ goto end;
+ }
+ break;
+
+ case IPSECDOI_ATTR_ENC_MODE:
+ if (pr->encmode &&
+ pr->encmode != (u_int16_t)ntohs(d->lorv)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "multiple encmode exist "
+ "in a transform.\n");
+ goto end;
+ }
+ pr->encmode = (u_int16_t)ntohs(d->lorv);
+ break;
+
+ case IPSECDOI_ATTR_AUTH:
+ if (tr->authtype != IPSECDOI_ATTR_AUTH_NONE) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "multiple authtype exist "
+ "in a transform.\n");
+ goto end;
+ }
+ tr->authtype = (u_int16_t)ntohs(d->lorv);
+ break;
+
+ case IPSECDOI_ATTR_KEY_LENGTH:
+ if (pr->proto_id != IPSECDOI_PROTO_IPSEC_ESP) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "key length defined but not ESP");
+ goto end;
+ }
+ tr->encklen = ntohs(d->lorv);
+ break;
+#ifdef HAVE_SECCTX
+ case IPSECDOI_ATTR_SECCTX:
+ {
+ int len = ntohs(d->lorv);
+ memcpy(&pp->sctx, d + 1, len);
+ pp->sctx.ctx_strlen = ntohs(pp->sctx.ctx_strlen);
+ break;
+ }
+#endif /* HAVE_SECCTX */
+ case IPSECDOI_ATTR_KEY_ROUNDS:
+ case IPSECDOI_ATTR_COMP_DICT_SIZE:
+ case IPSECDOI_ATTR_COMP_PRIVALG:
+ default:
+ break;
+ }
+
+ prev = d;
+ if (flag) {
+ tlen -= sizeof(*d);
+ d = (struct isakmp_data *)((char *)d + sizeof(*d));
+ } else {
+ tlen -= (sizeof(*d) + ntohs(d->lorv));
+ d = (struct isakmp_data *)((caddr_t)d + sizeof(*d) + ntohs(d->lorv));
+ }
+ }
+
+ error = 0;
+end:
+ return error;
+}
+
+int
+ipsecdoi_authalg2trnsid(alg)
+ int alg;
+{
+ switch (alg) {
+ case IPSECDOI_ATTR_AUTH_HMAC_MD5:
+ return IPSECDOI_AH_MD5;
+ case IPSECDOI_ATTR_AUTH_HMAC_SHA1:
+ return IPSECDOI_AH_SHA;
+ case IPSECDOI_ATTR_AUTH_HMAC_SHA2_256:
+ return IPSECDOI_AH_SHA256;
+ case IPSECDOI_ATTR_AUTH_HMAC_SHA2_384:
+ return IPSECDOI_AH_SHA384;
+ case IPSECDOI_ATTR_AUTH_HMAC_SHA2_512:
+ return IPSECDOI_AH_SHA512;
+ case IPSECDOI_ATTR_AUTH_DES_MAC:
+ return IPSECDOI_AH_DES;
+ case IPSECDOI_ATTR_AUTH_KPDK:
+ return IPSECDOI_AH_MD5; /* XXX */
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid authentication algorithm:%d\n", alg);
+ }
+ return -1;
+}
+
+static int rm_idtype2doi[] = {
+ 255, /* IDTYPE_UNDEFINED, 0 */
+ IPSECDOI_ID_FQDN, /* IDTYPE_FQDN, 1 */
+ IPSECDOI_ID_USER_FQDN, /* IDTYPE_USERFQDN, 2 */
+ IPSECDOI_ID_KEY_ID, /* IDTYPE_KEYID, 3 */
+ 255, /* IDTYPE_ADDRESS, 4
+ * it expands into 4 types by another function. */
+ IPSECDOI_ID_DER_ASN1_DN, /* IDTYPE_ASN1DN, 5 */
+};
+
+/*
+ * convert idtype to DOI value.
+ * OUT 255 : NG
+ * other: converted.
+ */
+int
+idtype2doi(idtype)
+ int idtype;
+{
+ if (ARRAYLEN(rm_idtype2doi) > idtype)
+ return rm_idtype2doi[idtype];
+ return 255;
+}
+
+int
+doi2idtype(doi)
+ int doi;
+{
+ switch(doi) {
+ case IPSECDOI_ID_FQDN:
+ return(IDTYPE_FQDN);
+ case IPSECDOI_ID_USER_FQDN:
+ return(IDTYPE_USERFQDN);
+ case IPSECDOI_ID_KEY_ID:
+ return(IDTYPE_KEYID);
+ case IPSECDOI_ID_DER_ASN1_DN:
+ return(IDTYPE_ASN1DN);
+ case IPSECDOI_ID_IPV4_ADDR:
+ case IPSECDOI_ID_IPV4_ADDR_SUBNET:
+ case IPSECDOI_ID_IPV6_ADDR:
+ case IPSECDOI_ID_IPV6_ADDR_SUBNET:
+ return(IDTYPE_ADDRESS);
+ default:
+ plog(LLV_WARNING, LOCATION, NULL,
+ "Inproper idtype:%s in this function.\n",
+ s_ipsecdoi_ident(doi));
+ return(IDTYPE_ADDRESS); /* XXX */
+ }
+ /*NOTREACHED*/
+}