summaryrefslogtreecommitdiffstats
path: root/ipsec-tools/src/racoon/proposal.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipsec-tools/src/racoon/proposal.c')
-rw-r--r--ipsec-tools/src/racoon/proposal.c1290
1 files changed, 1290 insertions, 0 deletions
diff --git a/ipsec-tools/src/racoon/proposal.c b/ipsec-tools/src/racoon/proposal.c
new file mode 100644
index 00000000..33dd3114
--- /dev/null
+++ b/ipsec-tools/src/racoon/proposal.c
@@ -0,0 +1,1290 @@
+/* $NetBSD: proposal.c,v 1.17 2008/09/19 11:14:49 tteras Exp $ */
+
+/* $Id: proposal.c,v 1.17 2008/09/19 11:14:49 tteras 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/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#include <netinet/in.h>
+#include PATH_IPSEC_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "var.h"
+#include "misc.h"
+#include "vmbuf.h"
+#include "plog.h"
+#include "sockmisc.h"
+#include "debug.h"
+
+#include "policy.h"
+#include "pfkey.h"
+#include "isakmp_var.h"
+#include "isakmp.h"
+#include "ipsec_doi.h"
+#include "algorithm.h"
+#include "proposal.h"
+#include "sainfo.h"
+#include "localconf.h"
+#include "remoteconf.h"
+#include "oakley.h"
+#include "handler.h"
+#include "strnames.h"
+#include "gcmalloc.h"
+#ifdef ENABLE_NATT
+#include "nattraversal.h"
+#endif
+
+static uint g_nextreqid = 1;
+
+/* %%%
+ * modules for ipsec sa spec
+ */
+struct saprop *
+newsaprop()
+{
+ struct saprop *new;
+
+ new = racoon_calloc(1, sizeof(*new));
+ if (new == NULL)
+ return NULL;
+
+ return new;
+}
+
+struct saproto *
+newsaproto()
+{
+ struct saproto *new;
+
+ new = racoon_calloc(1, sizeof(*new));
+ if (new == NULL)
+ return NULL;
+
+ return new;
+}
+
+/* set saprop to last part of the prop tree */
+void
+inssaprop(head, new)
+ struct saprop **head;
+ struct saprop *new;
+{
+ struct saprop *p;
+
+ if (*head == NULL) {
+ *head = new;
+ return;
+ }
+
+ for (p = *head; p->next; p = p->next)
+ ;
+ p->next = new;
+
+ return;
+}
+
+/* set saproto to the end of the proto tree in saprop */
+void
+inssaproto(pp, new)
+ struct saprop *pp;
+ struct saproto *new;
+{
+ struct saproto *p;
+
+ for (p = pp->head; p && p->next; p = p->next)
+ ;
+ if (p == NULL)
+ pp->head = new;
+ else
+ p->next = new;
+
+ return;
+}
+
+/* set saproto to the top of the proto tree in saprop */
+void
+inssaprotorev(pp, new)
+ struct saprop *pp;
+ struct saproto *new;
+{
+ new->next = pp->head;
+ pp->head = new;
+
+ return;
+}
+
+struct satrns *
+newsatrns()
+{
+ struct satrns *new;
+
+ new = racoon_calloc(1, sizeof(*new));
+ if (new == NULL)
+ return NULL;
+
+ return new;
+}
+
+/* set saproto to last part of the proto tree in saprop */
+void
+inssatrns(pr, new)
+ struct saproto *pr;
+ struct satrns *new;
+{
+ struct satrns *tr;
+
+ for (tr = pr->head; tr && tr->next; tr = tr->next)
+ ;
+ if (tr == NULL)
+ pr->head = new;
+ else
+ tr->next = new;
+
+ return;
+}
+
+/*
+ * take a single match between saprop. allocate a new proposal and return it
+ * for future use (like picking single proposal from a bundle).
+ * pp1: peer's proposal.
+ * pp2: my proposal.
+ * NOTE: In the case of initiator, must be ensured that there is no
+ * modification of the proposal by calling cmp_aproppair_i() before
+ * this function.
+ * XXX cannot understand the comment!
+ */
+struct saprop *
+cmpsaprop_alloc(ph1, pp1, pp2, side)
+ struct ph1handle *ph1;
+ const struct saprop *pp1, *pp2;
+ int side;
+{
+ struct saprop *newpp = NULL;
+ struct saproto *pr1, *pr2, *newpr = NULL;
+ struct satrns *tr1, *tr2, *newtr;
+ const int ordermatters = 0;
+ int npr1, npr2;
+ int spisizematch;
+
+ newpp = newsaprop();
+ if (newpp == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate saprop.\n");
+ return NULL;
+ }
+ newpp->prop_no = pp1->prop_no;
+
+ /* see proposal.h about lifetime/key length and PFS selection. */
+
+ /* check time/bytes lifetime and PFS */
+ switch (ph1->rmconf->pcheck_level) {
+ case PROP_CHECK_OBEY:
+ newpp->lifetime = pp1->lifetime;
+ newpp->lifebyte = pp1->lifebyte;
+ newpp->pfs_group = pp1->pfs_group;
+ break;
+
+ case PROP_CHECK_STRICT:
+ if (pp1->lifetime > pp2->lifetime) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "long lifetime proposed: "
+ "my:%d peer:%d\n",
+ (int)pp2->lifetime, (int)pp1->lifetime);
+ goto err;
+ }
+ if (pp1->lifebyte > pp2->lifebyte) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "long lifebyte proposed: "
+ "my:%d peer:%d\n",
+ pp2->lifebyte, pp1->lifebyte);
+ goto err;
+ }
+ newpp->lifetime = pp1->lifetime;
+ newpp->lifebyte = pp1->lifebyte;
+
+ prop_pfs_check:
+ if (pp2->pfs_group != 0 && pp1->pfs_group != pp2->pfs_group) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "pfs group mismatched: "
+ "my:%d peer:%d\n",
+ pp2->pfs_group, pp1->pfs_group);
+ goto err;
+ }
+ newpp->pfs_group = pp1->pfs_group;
+ break;
+
+ case PROP_CHECK_CLAIM:
+ /* lifetime */
+ if (pp1->lifetime <= pp2->lifetime) {
+ newpp->lifetime = pp1->lifetime;
+ } else {
+ newpp->lifetime = pp2->lifetime;
+ newpp->claim |= IPSECDOI_ATTR_SA_LD_TYPE_SEC;
+ plog(LLV_NOTIFY, LOCATION, NULL,
+ "use own lifetime: "
+ "my:%d peer:%d\n",
+ (int)pp2->lifetime, (int)pp1->lifetime);
+ }
+
+ /* lifebyte */
+ if (pp1->lifebyte > pp2->lifebyte) {
+ newpp->lifebyte = pp2->lifebyte;
+ newpp->claim |= IPSECDOI_ATTR_SA_LD_TYPE_SEC;
+ plog(LLV_NOTIFY, LOCATION, NULL,
+ "use own lifebyte: "
+ "my:%d peer:%d\n",
+ pp2->lifebyte, pp1->lifebyte);
+ }
+ newpp->lifebyte = pp1->lifebyte;
+
+ goto prop_pfs_check;
+ break;
+
+ case PROP_CHECK_EXACT:
+ if (pp1->lifetime != pp2->lifetime) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "lifetime mismatched: "
+ "my:%d peer:%d\n",
+ (int)pp2->lifetime, (int)pp1->lifetime);
+ goto err;
+ }
+
+ if (pp1->lifebyte != pp2->lifebyte) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "lifebyte mismatched: "
+ "my:%d peer:%d\n",
+ pp2->lifebyte, pp1->lifebyte);
+ goto err;
+ }
+ if (pp1->pfs_group != pp2->pfs_group) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "pfs group mismatched: "
+ "my:%d peer:%d\n",
+ pp2->pfs_group, pp1->pfs_group);
+ goto err;
+ }
+ newpp->lifetime = pp1->lifetime;
+ newpp->lifebyte = pp1->lifebyte;
+ newpp->pfs_group = pp1->pfs_group;
+ break;
+
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid pcheck_level why?.\n");
+ goto err;
+ }
+
+#ifdef HAVE_SECCTX
+ /* check the security_context properties.
+ * It is possible for one side to have a security context
+ * and the other side doesn't. If so, this is an error.
+ */
+
+ if (*pp1->sctx.ctx_str && !(*pp2->sctx.ctx_str)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "My proposal missing security context\n");
+ goto err;
+ }
+ if (!(*pp1->sctx.ctx_str) && *pp2->sctx.ctx_str) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Peer is missing security context\n");
+ goto err;
+ }
+
+ if (*pp1->sctx.ctx_str && *pp2->sctx.ctx_str) {
+ if (pp1->sctx.ctx_doi == pp2->sctx.ctx_doi)
+ newpp->sctx.ctx_doi = pp1->sctx.ctx_doi;
+ else {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "sec doi mismatched: my:%d peer:%d\n",
+ pp2->sctx.ctx_doi, pp1->sctx.ctx_doi);
+ goto err;
+ }
+
+ if (pp1->sctx.ctx_alg == pp2->sctx.ctx_alg)
+ newpp->sctx.ctx_alg = pp1->sctx.ctx_alg;
+ else {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "sec alg mismatched: my:%d peer:%d\n",
+ pp2->sctx.ctx_alg, pp1->sctx.ctx_alg);
+ goto err;
+ }
+
+ if ((pp1->sctx.ctx_strlen != pp2->sctx.ctx_strlen) ||
+ memcmp(pp1->sctx.ctx_str, pp2->sctx.ctx_str,
+ pp1->sctx.ctx_strlen) != 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "sec ctx string mismatched: my:%s peer:%s\n",
+ pp2->sctx.ctx_str, pp1->sctx.ctx_str);
+ goto err;
+ } else {
+ newpp->sctx.ctx_strlen = pp1->sctx.ctx_strlen;
+ memcpy(newpp->sctx.ctx_str, pp1->sctx.ctx_str,
+ pp1->sctx.ctx_strlen);
+ }
+ }
+#endif /* HAVE_SECCTX */
+
+ npr1 = npr2 = 0;
+ for (pr1 = pp1->head; pr1; pr1 = pr1->next)
+ npr1++;
+ for (pr2 = pp2->head; pr2; pr2 = pr2->next)
+ npr2++;
+ if (npr1 != npr2)
+ goto err;
+
+ /* check protocol order */
+ pr1 = pp1->head;
+ pr2 = pp2->head;
+
+ while (1) {
+ if (!ordermatters) {
+ /*
+ * XXX does not work if we have multiple proposals
+ * with the same proto_id
+ */
+ switch (side) {
+ case RESPONDER:
+ if (!pr2)
+ break;
+ for (pr1 = pp1->head; pr1; pr1 = pr1->next) {
+ if (pr1->proto_id == pr2->proto_id)
+ break;
+ }
+ break;
+ case INITIATOR:
+ if (!pr1)
+ break;
+ for (pr2 = pp2->head; pr2; pr2 = pr2->next) {
+ if (pr2->proto_id == pr1->proto_id)
+ break;
+ }
+ break;
+ }
+ }
+ if (!pr1 || !pr2)
+ break;
+
+ if (pr1->proto_id != pr2->proto_id) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "proto_id mismatched: "
+ "my:%s peer:%s\n",
+ s_ipsecdoi_proto(pr2->proto_id),
+ s_ipsecdoi_proto(pr1->proto_id));
+ goto err;
+ }
+ spisizematch = 0;
+ if (pr1->spisize == pr2->spisize)
+ spisizematch = 1;
+ else if (pr1->proto_id == IPSECDOI_PROTO_IPCOMP) {
+ /*
+ * draft-shacham-ippcp-rfc2393bis-05.txt:
+ * need to accept 16bit and 32bit SPI (CPI) for IPComp.
+ */
+ if (pr1->spisize == sizeof(u_int16_t) &&
+ pr2->spisize == sizeof(u_int32_t)) {
+ spisizematch = 1;
+ } else if (pr2->spisize == sizeof(u_int16_t) &&
+ pr1->spisize == sizeof(u_int32_t)) {
+ spisizematch = 1;
+ }
+ if (spisizematch) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "IPComp SPI size promoted "
+ "from 16bit to 32bit\n");
+ }
+ }
+ if (!spisizematch) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "spisize mismatched: "
+ "my:%d peer:%d\n",
+ (int)pr2->spisize, (int)pr1->spisize);
+ goto err;
+ }
+
+#ifdef ENABLE_NATT
+ if ((ph1->natt_flags & NAT_DETECTED) &&
+ natt_udp_encap (pr2->encmode))
+ {
+ plog(LLV_INFO, LOCATION, NULL, "Adjusting my encmode %s->%s\n",
+ s_ipsecdoi_encmode(pr2->encmode),
+ s_ipsecdoi_encmode(pr2->encmode - ph1->natt_options->mode_udp_diff));
+ pr2->encmode -= ph1->natt_options->mode_udp_diff;
+ pr2->udp_encap = 1;
+ }
+
+ if ((ph1->natt_flags & NAT_DETECTED) &&
+ natt_udp_encap (pr1->encmode))
+ {
+ plog(LLV_INFO, LOCATION, NULL, "Adjusting peer's encmode %s(%d)->%s(%d)\n",
+ s_ipsecdoi_encmode(pr1->encmode),
+ pr1->encmode,
+ s_ipsecdoi_encmode(pr1->encmode - ph1->natt_options->mode_udp_diff),
+ pr1->encmode - ph1->natt_options->mode_udp_diff);
+ pr1->encmode -= ph1->natt_options->mode_udp_diff;
+ pr1->udp_encap = 1;
+ }
+#endif
+
+ if (pr1->encmode != pr2->encmode) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "encmode mismatched: "
+ "my:%s peer:%s\n",
+ s_ipsecdoi_encmode(pr2->encmode),
+ s_ipsecdoi_encmode(pr1->encmode));
+ goto err;
+ }
+
+ for (tr1 = pr1->head; tr1; tr1 = tr1->next) {
+ for (tr2 = pr2->head; tr2; tr2 = tr2->next) {
+ if (cmpsatrns(pr1->proto_id, tr1, tr2, ph1->rmconf->pcheck_level) == 0)
+ goto found;
+ }
+ }
+
+ goto err;
+
+ found:
+ newpr = newsaproto();
+ if (newpr == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate saproto.\n");
+ goto err;
+ }
+ newpr->proto_id = pr1->proto_id;
+ newpr->spisize = pr1->spisize;
+ newpr->encmode = pr1->encmode;
+ newpr->spi = pr2->spi; /* copy my SPI */
+ newpr->spi_p = pr1->spi; /* copy peer's SPI */
+ newpr->reqid_in = pr2->reqid_in;
+ newpr->reqid_out = pr2->reqid_out;
+#ifdef ENABLE_NATT
+ newpr->udp_encap = pr1->udp_encap | pr2->udp_encap;
+#endif
+
+ newtr = newsatrns();
+ if (newtr == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate satrns.\n");
+ racoon_free(newpr);
+ goto err;
+ }
+ newtr->trns_no = tr1->trns_no;
+ newtr->trns_id = tr1->trns_id;
+ newtr->encklen = tr1->encklen;
+ newtr->authtype = tr1->authtype;
+
+ inssatrns(newpr, newtr);
+ inssaproto(newpp, newpr);
+
+ pr1 = pr1->next;
+ pr2 = pr2->next;
+ }
+
+ /* XXX should check if we have visited all items or not */
+ if (!ordermatters) {
+ switch (side) {
+ case RESPONDER:
+ if (!pr2)
+ pr1 = NULL;
+ break;
+ case INITIATOR:
+ if (!pr1)
+ pr2 = NULL;
+ break;
+ }
+ }
+
+ /* should be matched all protocols in a proposal */
+ if (pr1 != NULL || pr2 != NULL)
+ goto err;
+
+ return newpp;
+
+err:
+ flushsaprop(newpp);
+ return NULL;
+}
+
+/* take a single match between saprop. returns 0 if pp1 equals to pp2. */
+int
+cmpsaprop(pp1, pp2)
+ const struct saprop *pp1, *pp2;
+{
+ if (pp1->pfs_group != pp2->pfs_group) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "pfs_group mismatch. mine:%d peer:%d\n",
+ pp1->pfs_group, pp2->pfs_group);
+ /* FALLTHRU */
+ }
+
+ if (pp1->lifetime > pp2->lifetime) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "less lifetime proposed. mine:%d peer:%d\n",
+ (int)pp1->lifetime, (int)pp2->lifetime);
+ /* FALLTHRU */
+ }
+ if (pp1->lifebyte > pp2->lifebyte) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "less lifebyte proposed. mine:%d peer:%d\n",
+ pp1->lifebyte, pp2->lifebyte);
+ /* FALLTHRU */
+ }
+
+ return 0;
+}
+
+/*
+ * take a single match between satrns. returns 0 if tr1 equals to tr2.
+ * tr1: peer's satrns
+ * tr2: my satrns
+ */
+int
+cmpsatrns(proto_id, tr1, tr2, check_level)
+ int proto_id;
+ const struct satrns *tr1, *tr2;
+ int check_level;
+{
+ if (tr1->trns_id != tr2->trns_id) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "trns_id mismatched: "
+ "my:%s peer:%s\n",
+ s_ipsecdoi_trns(proto_id, tr2->trns_id),
+ s_ipsecdoi_trns(proto_id, tr1->trns_id));
+ return 1;
+ }
+
+ if (tr1->authtype != tr2->authtype) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "authtype mismatched: "
+ "my:%s peer:%s\n",
+ s_ipsecdoi_attr_v(IPSECDOI_ATTR_AUTH, tr2->authtype),
+ s_ipsecdoi_attr_v(IPSECDOI_ATTR_AUTH, tr1->authtype));
+ return 1;
+ }
+
+ /* Check key length regarding checkmode
+ * XXX Shall we send some kind of notify message when key length rejected ?
+ */
+ switch(check_level){
+ case PROP_CHECK_OBEY:
+ return 0;
+ break;
+
+ case PROP_CHECK_STRICT:
+ /* FALLTHROUGH */
+ case PROP_CHECK_CLAIM:
+ if (tr1->encklen < tr2->encklen) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "low key length proposed, "
+ "mine:%d peer:%d.\n",
+ tr2->encklen, tr1->encklen);
+ return 1;
+ }
+ break;
+ case PROP_CHECK_EXACT:
+ if (tr1->encklen != tr2->encklen) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "key length mismatched, "
+ "mine:%d peer:%d.\n",
+ tr2->encklen, tr1->encklen);
+ return 1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+int
+set_satrnsbysainfo(pr, sainfo)
+ struct saproto *pr;
+ struct sainfo *sainfo;
+{
+ struct sainfoalg *a, *b;
+ struct satrns *newtr;
+ int t;
+
+ switch (pr->proto_id) {
+ case IPSECDOI_PROTO_IPSEC_AH:
+ if (sainfo->algs[algclass_ipsec_auth] == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "no auth algorithm found\n");
+ goto err;
+ }
+ t = 1;
+ for (a = sainfo->algs[algclass_ipsec_auth]; a; a = a->next) {
+
+ if (a->alg == IPSECDOI_ATTR_AUTH_NONE)
+ continue;
+
+ /* allocate satrns */
+ newtr = newsatrns();
+ if (newtr == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate satrns.\n");
+ goto err;
+ }
+
+ newtr->trns_no = t++;
+ newtr->trns_id = ipsecdoi_authalg2trnsid(a->alg);
+ newtr->authtype = a->alg;
+
+ inssatrns(pr, newtr);
+ }
+ break;
+ case IPSECDOI_PROTO_IPSEC_ESP:
+ if (sainfo->algs[algclass_ipsec_enc] == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "no encryption algorithm found\n");
+ goto err;
+ }
+ t = 1;
+ for (a = sainfo->algs[algclass_ipsec_enc]; a; a = a->next) {
+ for (b = sainfo->algs[algclass_ipsec_auth]; b; b = b->next) {
+ /* allocate satrns */
+ newtr = newsatrns();
+ if (newtr == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate satrns.\n");
+ goto err;
+ }
+
+ newtr->trns_no = t++;
+ newtr->trns_id = a->alg;
+ newtr->encklen = a->encklen;
+ newtr->authtype = b->alg;
+
+ inssatrns(pr, newtr);
+ }
+ }
+ break;
+ case IPSECDOI_PROTO_IPCOMP:
+ if (sainfo->algs[algclass_ipsec_comp] == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "no ipcomp algorithm found\n");
+ goto err;
+ }
+ t = 1;
+ for (a = sainfo->algs[algclass_ipsec_comp]; a; a = a->next) {
+
+ /* allocate satrns */
+ newtr = newsatrns();
+ if (newtr == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate satrns.\n");
+ goto err;
+ }
+
+ newtr->trns_no = t++;
+ newtr->trns_id = a->alg;
+ newtr->authtype = IPSECDOI_ATTR_AUTH_NONE; /*no auth*/
+
+ inssatrns(pr, newtr);
+ }
+ break;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "unknown proto_id (%d).\n", pr->proto_id);
+ goto err;
+ }
+
+ /* no proposal found */
+ if (pr->head == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL, "no algorithms found.\n");
+ return -1;
+ }
+
+ return 0;
+
+err:
+ flushsatrns(pr->head);
+ return -1;
+}
+
+struct saprop *
+aproppair2saprop(p0)
+ struct prop_pair *p0;
+{
+ struct prop_pair *p, *t;
+ struct saprop *newpp;
+ struct saproto *newpr;
+ struct satrns *newtr;
+ u_int8_t *spi;
+
+ if (p0 == NULL)
+ return NULL;
+
+ /* allocate ipsec a sa proposal */
+ newpp = newsaprop();
+ if (newpp == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate saprop.\n");
+ return NULL;
+ }
+ newpp->prop_no = p0->prop->p_no;
+ /* lifetime & lifebyte must be updated later */
+
+ for (p = p0; p; p = p->next) {
+
+ /* allocate ipsec sa protocol */
+ newpr = newsaproto();
+ if (newpr == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate saproto.\n");
+ goto err;
+ }
+
+ /* check spi size */
+ /* XXX should be handled isakmp cookie */
+ if (sizeof(newpr->spi) < p->prop->spi_size) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid spi size %d.\n", p->prop->spi_size);
+ racoon_free(newpr);
+ goto err;
+ }
+
+ /*
+ * XXX SPI bits are left-filled, for use with IPComp.
+ * we should be switching to variable-length spi field...
+ */
+ newpr->proto_id = p->prop->proto_id;
+ newpr->spisize = p->prop->spi_size;
+ memset(&newpr->spi, 0, sizeof(newpr->spi));
+ spi = (u_int8_t *)&newpr->spi;
+ spi += sizeof(newpr->spi);
+ spi -= p->prop->spi_size;
+ memcpy(spi, p->prop + 1, p->prop->spi_size);
+ newpr->reqid_in = 0;
+ newpr->reqid_out = 0;
+
+ for (t = p; t; t = t->tnext) {
+
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "prop#=%d prot-id=%s spi-size=%d "
+ "#trns=%d trns#=%d trns-id=%s\n",
+ t->prop->p_no,
+ s_ipsecdoi_proto(t->prop->proto_id),
+ t->prop->spi_size, t->prop->num_t,
+ t->trns->t_no,
+ s_ipsecdoi_trns(t->prop->proto_id,
+ t->trns->t_id));
+
+ /* allocate ipsec sa transform */
+ newtr = newsatrns();
+ if (newtr == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate satrns.\n");
+ racoon_free(newpr);
+ goto err;
+ }
+
+ if (ipsecdoi_t2satrns(t->trns,
+ newpp, newpr, newtr) < 0) {
+ flushsaprop(newpp);
+ racoon_free(newtr);
+ racoon_free(newpr);
+ return NULL;
+ }
+
+ inssatrns(newpr, newtr);
+ }
+
+ /*
+ * If the peer does not specify encryption mode, use
+ * transport mode by default. This is to conform to
+ * draft-shacham-ippcp-rfc2393bis-08.txt (explicitly specifies
+ * that unspecified == transport), as well as RFC2407
+ * (unspecified == implementation dependent default).
+ */
+ if (newpr->encmode == 0)
+ newpr->encmode = IPSECDOI_ATTR_ENC_MODE_TRNS;
+
+ inssaproto(newpp, newpr);
+ }
+
+ return newpp;
+
+err:
+ flushsaprop(newpp);
+ return NULL;
+}
+
+void
+flushsaprop(head)
+ struct saprop *head;
+{
+ struct saprop *p, *save;
+
+ for (p = head; p != NULL; p = save) {
+ save = p->next;
+ flushsaproto(p->head);
+ racoon_free(p);
+ }
+
+ return;
+}
+
+void
+flushsaproto(head)
+ struct saproto *head;
+{
+ struct saproto *p, *save;
+
+ for (p = head; p != NULL; p = save) {
+ save = p->next;
+ flushsatrns(p->head);
+ vfree(p->keymat);
+ vfree(p->keymat_p);
+ racoon_free(p);
+ }
+
+ return;
+}
+
+void
+flushsatrns(head)
+ struct satrns *head;
+{
+ struct satrns *p, *save;
+
+ for (p = head; p != NULL; p = save) {
+ save = p->next;
+ racoon_free(p);
+ }
+
+ return;
+}
+
+/*
+ * print multiple proposals
+ */
+void
+printsaprop(pri, pp)
+ const int pri;
+ const struct saprop *pp;
+{
+ const struct saprop *p;
+
+ if (pp == NULL) {
+ plog(pri, LOCATION, NULL, "(null)");
+ return;
+ }
+
+ for (p = pp; p; p = p->next) {
+ printsaprop0(pri, p);
+ }
+
+ return;
+}
+
+/*
+ * print one proposal.
+ */
+void
+printsaprop0(pri, pp)
+ int pri;
+ const struct saprop *pp;
+{
+ const struct saproto *p;
+
+ if (pp == NULL)
+ return;
+
+ for (p = pp->head; p; p = p->next) {
+ printsaproto(pri, p);
+ }
+
+ return;
+}
+
+void
+printsaproto(pri, pr)
+ const int pri;
+ const struct saproto *pr;
+{
+ struct satrns *tr;
+
+ if (pr == NULL)
+ return;
+
+ plog(pri, LOCATION, NULL,
+ " (proto_id=%s spisize=%d spi=%08lx spi_p=%08lx "
+ "encmode=%s reqid=%d:%d)\n",
+ s_ipsecdoi_proto(pr->proto_id),
+ (int)pr->spisize,
+ (unsigned long)ntohl(pr->spi),
+ (unsigned long)ntohl(pr->spi_p),
+ s_ipsecdoi_attr_v(IPSECDOI_ATTR_ENC_MODE, pr->encmode),
+ (int)pr->reqid_in, (int)pr->reqid_out);
+
+ for (tr = pr->head; tr; tr = tr->next) {
+ printsatrns(pri, pr->proto_id, tr);
+ }
+
+ return;
+}
+
+void
+printsatrns(pri, proto_id, tr)
+ const int pri;
+ const int proto_id;
+ const struct satrns *tr;
+{
+ if (tr == NULL)
+ return;
+
+ switch (proto_id) {
+ case IPSECDOI_PROTO_IPSEC_AH:
+ plog(pri, LOCATION, NULL,
+ " (trns_id=%s authtype=%s)\n",
+ s_ipsecdoi_trns(proto_id, tr->trns_id),
+ s_ipsecdoi_attr_v(IPSECDOI_ATTR_AUTH, tr->authtype));
+ break;
+ case IPSECDOI_PROTO_IPSEC_ESP:
+ plog(pri, LOCATION, NULL,
+ " (trns_id=%s encklen=%d authtype=%s)\n",
+ s_ipsecdoi_trns(proto_id, tr->trns_id),
+ tr->encklen,
+ s_ipsecdoi_attr_v(IPSECDOI_ATTR_AUTH, tr->authtype));
+ break;
+ case IPSECDOI_PROTO_IPCOMP:
+ plog(pri, LOCATION, NULL,
+ " (trns_id=%s)\n",
+ s_ipsecdoi_trns(proto_id, tr->trns_id));
+ break;
+ default:
+ plog(pri, LOCATION, NULL,
+ "(unknown proto_id %d)\n", proto_id);
+ }
+
+ return;
+}
+
+void
+print_proppair0(pri, p, level)
+ int pri;
+ struct prop_pair *p;
+ int level;
+{
+ char spc[21];
+
+ memset(spc, ' ', sizeof(spc));
+ spc[sizeof(spc) - 1] = '\0';
+ if (level < 20) {
+ spc[level] = '\0';
+ }
+
+ plog(pri, LOCATION, NULL,
+ "%s%p: next=%p tnext=%p\n", spc, p, p->next, p->tnext);
+ if (p->next)
+ print_proppair0(pri, p->next, level + 1);
+ if (p->tnext)
+ print_proppair0(pri, p->tnext, level + 1);
+}
+
+void
+print_proppair(pri, p)
+ int pri;
+ struct prop_pair *p;
+{
+ print_proppair0(pri, p, 1);
+}
+
+int
+set_proposal_from_policy(iph2, sp_main, sp_sub)
+ struct ph2handle *iph2;
+ struct secpolicy *sp_main, *sp_sub;
+{
+ struct saprop *newpp;
+ struct ipsecrequest *req;
+ int encmodesv = IPSECDOI_ATTR_ENC_MODE_TRNS; /* use only when complex_bundle */
+
+ newpp = newsaprop();
+ if (newpp == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate saprop.\n");
+ goto err;
+ }
+ newpp->prop_no = 1;
+ newpp->lifetime = iph2->sainfo->lifetime;
+ newpp->lifebyte = iph2->sainfo->lifebyte;
+ newpp->pfs_group = iph2->sainfo->pfs_group;
+
+ if (lcconf->complex_bundle)
+ goto skip1;
+
+ /*
+ * decide the encryption mode of this SA bundle.
+ * the mode becomes tunnel mode when there is even one policy
+ * of tunnel mode in the SPD. otherwise the mode becomes
+ * transport mode.
+ */
+ for (req = sp_main->req; req; req = req->next) {
+ if (req->saidx.mode == IPSEC_MODE_TUNNEL) {
+ encmodesv = pfkey2ipsecdoi_mode(req->saidx.mode);
+#ifdef ENABLE_NATT
+ if (iph2->ph1 && (iph2->ph1->natt_flags & NAT_DETECTED))
+ encmodesv += iph2->ph1->natt_options->mode_udp_diff;
+#endif
+ break;
+ }
+ }
+
+ skip1:
+ for (req = sp_main->req; req; req = req->next) {
+ struct saproto *newpr;
+ caddr_t paddr = NULL;
+
+ /*
+ * check if SA bundle ?
+ * nested SAs negotiation is NOT supported.
+ * me +--- SA1 ---+ peer1
+ * me +--- SA2 --------------+ peer2
+ */
+#ifdef __linux__
+ if (req->saidx.src.ss_family && req->saidx.dst.ss_family) {
+#else
+ if (req->saidx.src.ss_len && req->saidx.dst.ss_len) {
+#endif
+ /* check the end of ip addresses of SA */
+ if (iph2->side == INITIATOR)
+ paddr = (caddr_t)&req->saidx.dst;
+ else
+ paddr = (caddr_t)&req->saidx.src;
+ }
+
+ /* allocate ipsec sa protocol */
+ newpr = newsaproto();
+ if (newpr == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate saproto.\n");
+ goto err;
+ }
+
+ newpr->proto_id = ipproto2doi(req->saidx.proto);
+ if (newpr->proto_id == IPSECDOI_PROTO_IPCOMP)
+ newpr->spisize = 2;
+ else
+ newpr->spisize = 4;
+ if (lcconf->complex_bundle) {
+ newpr->encmode = pfkey2ipsecdoi_mode(req->saidx.mode);
+#ifdef ENABLE_NATT
+ if (iph2->ph1 && (iph2->ph1->natt_flags & NAT_DETECTED))
+ newpr->encmode +=
+ iph2->ph1->natt_options->mode_udp_diff;
+#endif
+ }
+ else
+ newpr->encmode = encmodesv;
+
+ if (iph2->side == INITIATOR)
+ newpr->reqid_out = req->saidx.reqid;
+ else
+ newpr->reqid_in = req->saidx.reqid;
+
+ if (set_satrnsbysainfo(newpr, iph2->sainfo) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get algorithms.\n");
+ racoon_free(newpr);
+ goto err;
+ }
+
+ /* set new saproto */
+ inssaprotorev(newpp, newpr);
+ }
+
+ /* get reqid_in from inbound policy */
+ if (sp_sub) {
+ struct saproto *pr;
+
+ req = sp_sub->req;
+ pr = newpp->head;
+ while (req && pr) {
+ if (iph2->side == INITIATOR)
+ pr->reqid_in = req->saidx.reqid;
+ else
+ pr->reqid_out = req->saidx.reqid;
+ pr = pr->next;
+ req = req->next;
+ }
+ if (pr || req) {
+ plog(LLV_NOTIFY, LOCATION, NULL,
+ "There is a difference "
+ "between the in/out bound policies in SPD.\n");
+ }
+ }
+
+ iph2->proposal = newpp;
+
+ printsaprop0(LLV_DEBUG, newpp);
+
+ return 0;
+err:
+ flushsaprop(newpp);
+ return -1;
+}
+
+/*
+ * generate a policy from peer's proposal.
+ * this function unconditionally choices first proposal in SA payload
+ * passed by peer.
+ */
+int
+set_proposal_from_proposal(iph2)
+ struct ph2handle *iph2;
+{
+ struct saprop *newpp = NULL, *pp0, *pp_peer = NULL;
+ struct saproto *newpr = NULL, *pr;
+ struct prop_pair **pair;
+ int error = -1;
+ int i;
+
+ /* get proposal pair */
+ pair = get_proppair(iph2->sa, IPSECDOI_TYPE_PH2);
+ if (pair == NULL)
+ goto end;
+
+ /*
+ * make my proposal according as the client proposal.
+ * XXX assumed there is only one proposal even if it's the SA bundle.
+ */
+ for (i = 0; i < MAXPROPPAIRLEN; i++) {
+ if (pair[i] == NULL)
+ continue;
+
+ if (pp_peer != NULL)
+ flushsaprop(pp_peer);
+
+ pp_peer = aproppair2saprop(pair[i]);
+ if (pp_peer == NULL)
+ goto end;
+
+ pp0 = newsaprop();
+ if (pp0 == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate saprop.\n");
+ goto end;
+ }
+ pp0->prop_no = 1;
+ pp0->lifetime = iph2->sainfo->lifetime;
+ pp0->lifebyte = iph2->sainfo->lifebyte;
+ pp0->pfs_group = iph2->sainfo->pfs_group;
+
+#ifdef HAVE_SECCTX
+ if (*pp_peer->sctx.ctx_str) {
+ pp0->sctx.ctx_doi = pp_peer->sctx.ctx_doi;
+ pp0->sctx.ctx_alg = pp_peer->sctx.ctx_alg;
+ pp0->sctx.ctx_strlen = pp_peer->sctx.ctx_strlen;
+ memcpy(pp0->sctx.ctx_str, pp_peer->sctx.ctx_str,
+ pp_peer->sctx.ctx_strlen);
+ }
+#endif /* HAVE_SECCTX */
+
+ if (pp_peer->next != NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "pp_peer is inconsistency, ignore it.\n");
+ /*FALLTHROUGH*/
+ }
+
+ for (pr = pp_peer->head; pr; pr = pr->next)
+ {
+ newpr = newsaproto();
+ if (newpr == NULL)
+ {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate saproto.\n");
+ racoon_free(pp0);
+ goto end;
+ }
+ newpr->proto_id = pr->proto_id;
+ newpr->spisize = pr->spisize;
+ newpr->encmode = pr->encmode;
+ newpr->spi = 0;
+ newpr->spi_p = pr->spi; /* copy peer's SPI */
+ newpr->reqid_in = 0;
+ newpr->reqid_out = 0;
+
+ if (iph2->ph1->rmconf->gen_policy == GENERATE_POLICY_UNIQUE){
+ newpr->reqid_in = g_nextreqid ;
+ newpr->reqid_out = g_nextreqid ++;
+ /*
+ * XXX there is a (very limited)
+ * risk of reusing the same reqid
+ * as another SP entry for the same peer
+ */
+ if(g_nextreqid >= IPSEC_MANUAL_REQID_MAX)
+ g_nextreqid = 1;
+ }else{
+ newpr->reqid_in = 0;
+ newpr->reqid_out = 0;
+ }
+
+ if (set_satrnsbysainfo(newpr, iph2->sainfo) < 0)
+ {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get algorithms.\n");
+ racoon_free(newpr);
+ racoon_free(pp0);
+ goto end;
+ }
+ inssaproto(pp0, newpr);
+ }
+
+ inssaprop(&newpp, pp0);
+ }
+
+ plog(LLV_DEBUG, LOCATION, NULL, "make a proposal from peer's:\n");
+ printsaprop0(LLV_DEBUG, newpp);
+
+ iph2->proposal = newpp;
+
+ error = 0;
+
+end:
+ if (error && newpp)
+ flushsaprop(newpp);
+
+ if (pp_peer)
+ flushsaprop(pp_peer);
+ if (pair)
+ free_proppair(pair);
+ return error;
+}