/* $NetBSD: proposal.c,v 1.13.4.2 2008/07/22 13:25:42 vanhu Exp $ */ /* $Id: proposal.c,v 1.13.4.2 2008/07/22 13:25:42 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/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) { struct remoteconf *conf; 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; conf = getrmconf(iph2->dst); if (conf != NULL && conf->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; }