diff options
Diffstat (limited to 'ipsec-tools/src/racoon/proposal.c')
-rw-r--r-- | ipsec-tools/src/racoon/proposal.c | 1290 |
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; +} |