1128 lines
24 KiB
C
1128 lines
24 KiB
C
/* $KAME: proposal.c,v 1.50 2004/04/08 05:39:33 sakane 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 <sys/cdefs.h>
|
|
__RCSID("$NetBSD: proposal.c,v 1.5 2004/04/12 03:34:07 itojun Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/queue.h>
|
|
|
|
#include <netkey/key_var.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet6/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"
|
|
|
|
/* %%%
|
|
* 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",
|
|
pp2->lifetime, 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",
|
|
pp2->lifetime, 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",
|
|
pp2->lifetime, 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->lifebyte = pp1->lifebyte;
|
|
newpp->lifetime = pp1->lifetime;
|
|
newpp->pfs_group = pp1->pfs_group;
|
|
break;
|
|
default:
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"invalid pcheck_level why?.\n");
|
|
goto err;
|
|
}
|
|
|
|
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 (pr1->spisize == sizeof(u_int16_t) &&
|
|
pr2->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",
|
|
pr2->spisize, pr1->spisize);
|
|
goto err;
|
|
}
|
|
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(tr1, tr2) == 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;
|
|
|
|
newtr = newsatrns();
|
|
if (newtr == NULL) {
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"failed to allocate satrns.\n");
|
|
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",
|
|
pp1->lifetime, 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(tr1, tr2)
|
|
const struct satrns *tr1, *tr2;
|
|
{
|
|
if (tr1->trns_id != tr2->trns_id) {
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"trns_id mismatched: "
|
|
"my:%d peer:%d\n",
|
|
tr2->trns_id, tr1->trns_id);
|
|
return 1;
|
|
}
|
|
if (tr1->authtype != tr2->authtype) {
|
|
plog(LLV_ERROR, 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;
|
|
}
|
|
|
|
/* XXX
|
|
* At this moment for interoperability, the responder obey
|
|
* the initiator. It should be defined a notify message.
|
|
*/
|
|
if (tr1->encklen > tr2->encklen) {
|
|
plog(LLV_WARNING, LOCATION, NULL,
|
|
"less key length proposed, "
|
|
"mine:%d peer:%d. Use initiaotr's one.\n",
|
|
tr2->encklen, tr1->encklen);
|
|
/* FALLTHRU */
|
|
}
|
|
|
|
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);
|
|
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");
|
|
goto err;
|
|
}
|
|
|
|
if (ipsecdoi_t2satrns(t->trns, newpp, newpr, newtr) < 0) {
|
|
flushsaprop(newpp);
|
|
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),
|
|
pr->spisize,
|
|
(unsigned long)ntohl(pr->spi),
|
|
(unsigned long)ntohl(pr->spi_p),
|
|
s_ipsecdoi_attr_v(IPSECDOI_ATTR_ENC_MODE, pr->encmode),
|
|
pr->reqid_in, 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 = IPSEC_MODE_TRANSPORT; /* 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.
|
|
*/
|
|
encmodesv = IPSEC_MODE_TRANSPORT;
|
|
for (req = sp_main->req; req; req = req->next) {
|
|
if (req->saidx.mode == IPSEC_MODE_TUNNEL) {
|
|
encmodesv = pfkey2ipsecdoi_mode(req->saidx.mode);
|
|
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
|
|
*/
|
|
if (req->saidx.src.ss_len && req->saidx.dst.ss_len) {
|
|
|
|
/* 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;
|
|
|
|
if (memcmp(iph2->dst, paddr, iph2->dst->sa_len)){
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"not supported nested SA.");
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
/* 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);
|
|
newpr->spisize = 4;
|
|
if (lcconf->complex_bundle)
|
|
newpr->encmode = pfkey2ipsecdoi_mode(req->saidx.mode);
|
|
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");
|
|
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:
|
|
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;
|
|
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;
|
|
|
|
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");
|
|
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 (set_satrnsbysainfo(newpr, iph2->sainfo) < 0) {
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"failed to get algorithms.\n");
|
|
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);
|
|
free_proppair(pair);
|
|
return error;
|
|
}
|