Home | History | Annotate | Download | only in racoon
      1 /*	$NetBSD: proposal.c,v 1.13.4.2 2008/07/22 13:25:42 vanhu Exp $	*/
      2 
      3 /* $Id: proposal.c,v 1.13.4.2 2008/07/22 13:25:42 vanhu Exp $ */
      4 
      5 /*
      6  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  * 3. Neither the name of the project nor the names of its contributors
     18  *    may be used to endorse or promote products derived from this software
     19  *    without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
     22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
     25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31  * SUCH DAMAGE.
     32  */
     33 
     34 #include "config.h"
     35 
     36 #include <sys/param.h>
     37 #include <sys/types.h>
     38 #include <sys/socket.h>
     39 #include <sys/queue.h>
     40 
     41 #include <netinet/in.h>
     42 #include PATH_IPSEC_H
     43 
     44 #include <stdlib.h>
     45 #include <stdio.h>
     46 #include <string.h>
     47 #include <errno.h>
     48 
     49 #include "var.h"
     50 #include "misc.h"
     51 #include "vmbuf.h"
     52 #include "plog.h"
     53 #include "sockmisc.h"
     54 #include "debug.h"
     55 
     56 #include "policy.h"
     57 #include "pfkey.h"
     58 #include "isakmp_var.h"
     59 #include "isakmp.h"
     60 #include "ipsec_doi.h"
     61 #include "algorithm.h"
     62 #include "proposal.h"
     63 #include "sainfo.h"
     64 #include "localconf.h"
     65 #include "remoteconf.h"
     66 #include "oakley.h"
     67 #include "handler.h"
     68 #include "strnames.h"
     69 #include "gcmalloc.h"
     70 #ifdef ENABLE_NATT
     71 #include "nattraversal.h"
     72 #endif
     73 
     74 static uint g_nextreqid = 1;
     75 
     76 /* %%%
     77  * modules for ipsec sa spec
     78  */
     79 struct saprop *
     80 newsaprop()
     81 {
     82 	struct saprop *new;
     83 
     84 	new = racoon_calloc(1, sizeof(*new));
     85 	if (new == NULL)
     86 		return NULL;
     87 
     88 	return new;
     89 }
     90 
     91 struct saproto *
     92 newsaproto()
     93 {
     94 	struct saproto *new;
     95 
     96 	new = racoon_calloc(1, sizeof(*new));
     97 	if (new == NULL)
     98 		return NULL;
     99 
    100 	return new;
    101 }
    102 
    103 /* set saprop to last part of the prop tree */
    104 void
    105 inssaprop(head, new)
    106 	struct saprop **head;
    107 	struct saprop *new;
    108 {
    109 	struct saprop *p;
    110 
    111 	if (*head == NULL) {
    112 		*head = new;
    113 		return;
    114 	}
    115 
    116 	for (p = *head; p->next; p = p->next)
    117 		;
    118 	p->next = new;
    119 
    120 	return;
    121 }
    122 
    123 /* set saproto to the end of the proto tree in saprop */
    124 void
    125 inssaproto(pp, new)
    126 	struct saprop *pp;
    127 	struct saproto *new;
    128 {
    129 	struct saproto *p;
    130 
    131 	for (p = pp->head; p && p->next; p = p->next)
    132 		;
    133 	if (p == NULL)
    134 		pp->head = new;
    135 	else
    136 		p->next = new;
    137 
    138 	return;
    139 }
    140 
    141 /* set saproto to the top of the proto tree in saprop */
    142 void
    143 inssaprotorev(pp, new)
    144       struct saprop *pp;
    145       struct saproto *new;
    146 {
    147       new->next = pp->head;
    148       pp->head = new;
    149 
    150       return;
    151 }
    152 
    153 struct satrns *
    154 newsatrns()
    155 {
    156 	struct satrns *new;
    157 
    158 	new = racoon_calloc(1, sizeof(*new));
    159 	if (new == NULL)
    160 		return NULL;
    161 
    162 	return new;
    163 }
    164 
    165 /* set saproto to last part of the proto tree in saprop */
    166 void
    167 inssatrns(pr, new)
    168 	struct saproto *pr;
    169 	struct satrns *new;
    170 {
    171 	struct satrns *tr;
    172 
    173 	for (tr = pr->head; tr && tr->next; tr = tr->next)
    174 		;
    175 	if (tr == NULL)
    176 		pr->head = new;
    177 	else
    178 		tr->next = new;
    179 
    180 	return;
    181 }
    182 
    183 /*
    184  * take a single match between saprop.  allocate a new proposal and return it
    185  * for future use (like picking single proposal from a bundle).
    186  *	pp1: peer's proposal.
    187  *	pp2: my proposal.
    188  * NOTE: In the case of initiator, must be ensured that there is no
    189  * modification of the proposal by calling cmp_aproppair_i() before
    190  * this function.
    191  * XXX cannot understand the comment!
    192  */
    193 struct saprop *
    194 cmpsaprop_alloc(ph1, pp1, pp2, side)
    195 	struct ph1handle *ph1;
    196 	const struct saprop *pp1, *pp2;
    197 	int side;
    198 {
    199 	struct saprop *newpp = NULL;
    200 	struct saproto *pr1, *pr2, *newpr = NULL;
    201 	struct satrns *tr1, *tr2, *newtr;
    202 	const int ordermatters = 0;
    203 	int npr1, npr2;
    204 	int spisizematch;
    205 
    206 	newpp = newsaprop();
    207 	if (newpp == NULL) {
    208 		plog(LLV_ERROR, LOCATION, NULL,
    209 			"failed to allocate saprop.\n");
    210 		return NULL;
    211 	}
    212 	newpp->prop_no = pp1->prop_no;
    213 
    214 	/* see proposal.h about lifetime/key length and PFS selection. */
    215 
    216 	/* check time/bytes lifetime and PFS */
    217 	switch (ph1->rmconf->pcheck_level) {
    218 	case PROP_CHECK_OBEY:
    219 		newpp->lifetime = pp1->lifetime;
    220 		newpp->lifebyte = pp1->lifebyte;
    221 		newpp->pfs_group = pp1->pfs_group;
    222 		break;
    223 
    224 	case PROP_CHECK_STRICT:
    225 		if (pp1->lifetime > pp2->lifetime) {
    226 			plog(LLV_ERROR, LOCATION, NULL,
    227 				"long lifetime proposed: "
    228 				"my:%d peer:%d\n",
    229 				(int)pp2->lifetime, (int)pp1->lifetime);
    230 			goto err;
    231 		}
    232 		if (pp1->lifebyte > pp2->lifebyte) {
    233 			plog(LLV_ERROR, LOCATION, NULL,
    234 				"long lifebyte proposed: "
    235 				"my:%d peer:%d\n",
    236 				pp2->lifebyte, pp1->lifebyte);
    237 			goto err;
    238 		}
    239 		newpp->lifetime = pp1->lifetime;
    240 		newpp->lifebyte = pp1->lifebyte;
    241 
    242     prop_pfs_check:
    243 		if (pp2->pfs_group != 0 && pp1->pfs_group != pp2->pfs_group) {
    244 			plog(LLV_ERROR, LOCATION, NULL,
    245 				"pfs group mismatched: "
    246 				"my:%d peer:%d\n",
    247 				pp2->pfs_group, pp1->pfs_group);
    248 			goto err;
    249 		}
    250 		newpp->pfs_group = pp1->pfs_group;
    251 		break;
    252 
    253 	case PROP_CHECK_CLAIM:
    254 		/* lifetime */
    255 		if (pp1->lifetime <= pp2->lifetime) {
    256 			newpp->lifetime = pp1->lifetime;
    257 		} else {
    258 			newpp->lifetime = pp2->lifetime;
    259 			newpp->claim |= IPSECDOI_ATTR_SA_LD_TYPE_SEC;
    260 			plog(LLV_NOTIFY, LOCATION, NULL,
    261 				"use own lifetime: "
    262 				"my:%d peer:%d\n",
    263 				(int)pp2->lifetime, (int)pp1->lifetime);
    264 		}
    265 
    266 		/* lifebyte */
    267 		if (pp1->lifebyte > pp2->lifebyte) {
    268 			newpp->lifebyte = pp2->lifebyte;
    269 			newpp->claim |= IPSECDOI_ATTR_SA_LD_TYPE_SEC;
    270 			plog(LLV_NOTIFY, LOCATION, NULL,
    271 				"use own lifebyte: "
    272 				"my:%d peer:%d\n",
    273 				pp2->lifebyte, pp1->lifebyte);
    274 		}
    275 		newpp->lifebyte = pp1->lifebyte;
    276 
    277     		goto prop_pfs_check;
    278 		break;
    279 
    280 	case PROP_CHECK_EXACT:
    281 		if (pp1->lifetime != pp2->lifetime) {
    282 			plog(LLV_ERROR, LOCATION, NULL,
    283 				"lifetime mismatched: "
    284 				"my:%d peer:%d\n",
    285 				(int)pp2->lifetime, (int)pp1->lifetime);
    286 			goto err;
    287 		}
    288 
    289 		if (pp1->lifebyte != pp2->lifebyte) {
    290 			plog(LLV_ERROR, LOCATION, NULL,
    291 				"lifebyte mismatched: "
    292 				"my:%d peer:%d\n",
    293 				pp2->lifebyte, pp1->lifebyte);
    294 			goto err;
    295 		}
    296 		if (pp1->pfs_group != pp2->pfs_group) {
    297 			plog(LLV_ERROR, LOCATION, NULL,
    298 				"pfs group mismatched: "
    299 				"my:%d peer:%d\n",
    300 				pp2->pfs_group, pp1->pfs_group);
    301 			goto err;
    302 		}
    303 		newpp->lifetime = pp1->lifetime;
    304 		newpp->lifebyte = pp1->lifebyte;
    305 		newpp->pfs_group = pp1->pfs_group;
    306 		break;
    307 
    308 	default:
    309 		plog(LLV_ERROR, LOCATION, NULL,
    310 			"invalid pcheck_level why?.\n");
    311 		goto err;
    312 	}
    313 
    314 #ifdef HAVE_SECCTX
    315 	/* check the security_context properties.
    316 	 * It is possible for one side to have a security context
    317 	 * and the other side doesn't. If so, this is an error.
    318 	 */
    319 
    320 	if (*pp1->sctx.ctx_str && !(*pp2->sctx.ctx_str)) {
    321 		plog(LLV_ERROR, LOCATION, NULL,
    322 		     "My proposal missing security context\n");
    323 		goto err;
    324 	}
    325 	if (!(*pp1->sctx.ctx_str) && *pp2->sctx.ctx_str) {
    326 		plog(LLV_ERROR, LOCATION, NULL,
    327 		     "Peer is missing security context\n");
    328 		goto err;
    329 	}
    330 
    331 	if (*pp1->sctx.ctx_str && *pp2->sctx.ctx_str) {
    332 		if (pp1->sctx.ctx_doi == pp2->sctx.ctx_doi)
    333 			newpp->sctx.ctx_doi = pp1->sctx.ctx_doi;
    334 		else {
    335 			plog(LLV_ERROR, LOCATION, NULL,
    336 			     "sec doi mismatched: my:%d peer:%d\n",
    337 			     pp2->sctx.ctx_doi, pp1->sctx.ctx_doi);
    338 			     goto err;
    339 		}
    340 
    341 		if (pp1->sctx.ctx_alg == pp2->sctx.ctx_alg)
    342 			newpp->sctx.ctx_alg = pp1->sctx.ctx_alg;
    343 		else {
    344 			plog(LLV_ERROR, LOCATION, NULL,
    345 			     "sec alg mismatched: my:%d peer:%d\n",
    346 			     pp2->sctx.ctx_alg, pp1->sctx.ctx_alg);
    347 			goto err;
    348 		}
    349 
    350 		if ((pp1->sctx.ctx_strlen != pp2->sctx.ctx_strlen) ||
    351 		     memcmp(pp1->sctx.ctx_str, pp2->sctx.ctx_str,
    352 		     pp1->sctx.ctx_strlen) != 0) {
    353 			plog(LLV_ERROR, LOCATION, NULL,
    354 			     "sec ctx string mismatched: my:%s peer:%s\n",
    355 			     pp2->sctx.ctx_str, pp1->sctx.ctx_str);
    356 				goto err;
    357 		} else {
    358 			newpp->sctx.ctx_strlen = pp1->sctx.ctx_strlen;
    359 			memcpy(newpp->sctx.ctx_str, pp1->sctx.ctx_str,
    360 				pp1->sctx.ctx_strlen);
    361 		}
    362 	}
    363 #endif /* HAVE_SECCTX */
    364 
    365 	npr1 = npr2 = 0;
    366 	for (pr1 = pp1->head; pr1; pr1 = pr1->next)
    367 		npr1++;
    368 	for (pr2 = pp2->head; pr2; pr2 = pr2->next)
    369 		npr2++;
    370 	if (npr1 != npr2)
    371 		goto err;
    372 
    373 	/* check protocol order */
    374 	pr1 = pp1->head;
    375 	pr2 = pp2->head;
    376 
    377 	while (1) {
    378 		if (!ordermatters) {
    379 			/*
    380 			 * XXX does not work if we have multiple proposals
    381 			 * with the same proto_id
    382 			 */
    383 			switch (side) {
    384 			case RESPONDER:
    385 				if (!pr2)
    386 					break;
    387 				for (pr1 = pp1->head; pr1; pr1 = pr1->next) {
    388 					if (pr1->proto_id == pr2->proto_id)
    389 						break;
    390 				}
    391 				break;
    392 			case INITIATOR:
    393 				if (!pr1)
    394 					break;
    395 				for (pr2 = pp2->head; pr2; pr2 = pr2->next) {
    396 					if (pr2->proto_id == pr1->proto_id)
    397 						break;
    398 				}
    399 				break;
    400 			}
    401 		}
    402 		if (!pr1 || !pr2)
    403 			break;
    404 
    405 		if (pr1->proto_id != pr2->proto_id) {
    406 			plog(LLV_ERROR, LOCATION, NULL,
    407 				"proto_id mismatched: "
    408 				"my:%s peer:%s\n",
    409 				s_ipsecdoi_proto(pr2->proto_id),
    410 				s_ipsecdoi_proto(pr1->proto_id));
    411 			goto err;
    412 		}
    413 		spisizematch = 0;
    414 		if (pr1->spisize == pr2->spisize)
    415 			spisizematch = 1;
    416 		else if (pr1->proto_id == IPSECDOI_PROTO_IPCOMP) {
    417 			/*
    418 			 * draft-shacham-ippcp-rfc2393bis-05.txt:
    419 			 * need to accept 16bit and 32bit SPI (CPI) for IPComp.
    420 			 */
    421 			if (pr1->spisize == sizeof(u_int16_t) &&
    422 			    pr2->spisize == sizeof(u_int32_t)) {
    423 				spisizematch = 1;
    424 			} else if (pr2->spisize == sizeof(u_int16_t) &&
    425 				 pr1->spisize == sizeof(u_int32_t)) {
    426 				spisizematch = 1;
    427 			}
    428 			if (spisizematch) {
    429 				plog(LLV_ERROR, LOCATION, NULL,
    430 				    "IPComp SPI size promoted "
    431 				    "from 16bit to 32bit\n");
    432 			}
    433 		}
    434 		if (!spisizematch) {
    435 			plog(LLV_ERROR, LOCATION, NULL,
    436 				"spisize mismatched: "
    437 				"my:%d peer:%d\n",
    438 				(int)pr2->spisize, (int)pr1->spisize);
    439 			goto err;
    440 		}
    441 
    442 #ifdef ENABLE_NATT
    443 		if ((ph1->natt_flags & NAT_DETECTED) &&
    444 		    natt_udp_encap (pr2->encmode))
    445 		{
    446 			plog(LLV_INFO, LOCATION, NULL, "Adjusting my encmode %s->%s\n",
    447 			     s_ipsecdoi_encmode(pr2->encmode),
    448 			     s_ipsecdoi_encmode(pr2->encmode - ph1->natt_options->mode_udp_diff));
    449 			pr2->encmode -= ph1->natt_options->mode_udp_diff;
    450 			pr2->udp_encap = 1;
    451 		}
    452 
    453 		if ((ph1->natt_flags & NAT_DETECTED) &&
    454 		    natt_udp_encap (pr1->encmode))
    455 		{
    456 			plog(LLV_INFO, LOCATION, NULL, "Adjusting peer's encmode %s(%d)->%s(%d)\n",
    457 			     s_ipsecdoi_encmode(pr1->encmode),
    458 			     pr1->encmode,
    459 			     s_ipsecdoi_encmode(pr1->encmode - ph1->natt_options->mode_udp_diff),
    460 			     pr1->encmode - ph1->natt_options->mode_udp_diff);
    461 			pr1->encmode -= ph1->natt_options->mode_udp_diff;
    462 			pr1->udp_encap = 1;
    463 		}
    464 #endif
    465 
    466 		if (pr1->encmode != pr2->encmode) {
    467 			plog(LLV_ERROR, LOCATION, NULL,
    468 				"encmode mismatched: "
    469 				"my:%s peer:%s\n",
    470 				s_ipsecdoi_encmode(pr2->encmode),
    471 				s_ipsecdoi_encmode(pr1->encmode));
    472 			goto err;
    473 		}
    474 
    475 		for (tr1 = pr1->head; tr1; tr1 = tr1->next) {
    476 			for (tr2 = pr2->head; tr2; tr2 = tr2->next) {
    477 				if (cmpsatrns(pr1->proto_id, tr1, tr2, ph1->rmconf->pcheck_level) == 0)
    478 					goto found;
    479 			}
    480 		}
    481 
    482 		goto err;
    483 
    484 	    found:
    485 		newpr = newsaproto();
    486 		if (newpr == NULL) {
    487 			plog(LLV_ERROR, LOCATION, NULL,
    488 				"failed to allocate saproto.\n");
    489 			goto err;
    490 		}
    491 		newpr->proto_id = pr1->proto_id;
    492 		newpr->spisize = pr1->spisize;
    493 		newpr->encmode = pr1->encmode;
    494 		newpr->spi = pr2->spi;		/* copy my SPI */
    495 		newpr->spi_p = pr1->spi;	/* copy peer's SPI */
    496 		newpr->reqid_in = pr2->reqid_in;
    497 		newpr->reqid_out = pr2->reqid_out;
    498 #ifdef ENABLE_NATT
    499 		newpr->udp_encap = pr1->udp_encap | pr2->udp_encap;
    500 #endif
    501 
    502 		newtr = newsatrns();
    503 		if (newtr == NULL) {
    504 			plog(LLV_ERROR, LOCATION, NULL,
    505 				"failed to allocate satrns.\n");
    506 			racoon_free(newpr);
    507 			goto err;
    508 		}
    509 		newtr->trns_no = tr1->trns_no;
    510 		newtr->trns_id = tr1->trns_id;
    511 		newtr->encklen = tr1->encklen;
    512 		newtr->authtype = tr1->authtype;
    513 
    514 		inssatrns(newpr, newtr);
    515 		inssaproto(newpp, newpr);
    516 
    517 		pr1 = pr1->next;
    518 		pr2 = pr2->next;
    519 	}
    520 
    521 	/* XXX should check if we have visited all items or not */
    522 	if (!ordermatters) {
    523 		switch (side) {
    524 		case RESPONDER:
    525 			if (!pr2)
    526 				pr1 = NULL;
    527 			break;
    528 		case INITIATOR:
    529 			if (!pr1)
    530 				pr2 = NULL;
    531 			break;
    532 		}
    533 	}
    534 
    535 	/* should be matched all protocols in a proposal */
    536 	if (pr1 != NULL || pr2 != NULL)
    537 		goto err;
    538 
    539 	return newpp;
    540 
    541 err:
    542 	flushsaprop(newpp);
    543 	return NULL;
    544 }
    545 
    546 /* take a single match between saprop.  returns 0 if pp1 equals to pp2. */
    547 int
    548 cmpsaprop(pp1, pp2)
    549 	const struct saprop *pp1, *pp2;
    550 {
    551 	if (pp1->pfs_group != pp2->pfs_group) {
    552 		plog(LLV_WARNING, LOCATION, NULL,
    553 			"pfs_group mismatch. mine:%d peer:%d\n",
    554 			pp1->pfs_group, pp2->pfs_group);
    555 		/* FALLTHRU */
    556 	}
    557 
    558 	if (pp1->lifetime > pp2->lifetime) {
    559 		plog(LLV_WARNING, LOCATION, NULL,
    560 			"less lifetime proposed. mine:%d peer:%d\n",
    561 			(int)pp1->lifetime, (int)pp2->lifetime);
    562 		/* FALLTHRU */
    563 	}
    564 	if (pp1->lifebyte > pp2->lifebyte) {
    565 		plog(LLV_WARNING, LOCATION, NULL,
    566 			"less lifebyte proposed. mine:%d peer:%d\n",
    567 			pp1->lifebyte, pp2->lifebyte);
    568 		/* FALLTHRU */
    569 	}
    570 
    571 	return 0;
    572 }
    573 
    574 /*
    575  * take a single match between satrns.  returns 0 if tr1 equals to tr2.
    576  * tr1: peer's satrns
    577  * tr2: my satrns
    578  */
    579 int
    580 cmpsatrns(proto_id, tr1, tr2, check_level)
    581 	int proto_id;
    582 	const struct satrns *tr1, *tr2;
    583 	int check_level;
    584 {
    585 	if (tr1->trns_id != tr2->trns_id) {
    586 		plog(LLV_WARNING, LOCATION, NULL,
    587 			"trns_id mismatched: "
    588 			"my:%s peer:%s\n",
    589 			s_ipsecdoi_trns(proto_id, tr2->trns_id),
    590 			s_ipsecdoi_trns(proto_id, tr1->trns_id));
    591 		return 1;
    592 	}
    593 
    594 	if (tr1->authtype != tr2->authtype) {
    595 		plog(LLV_WARNING, LOCATION, NULL,
    596 			"authtype mismatched: "
    597 			"my:%s peer:%s\n",
    598 			s_ipsecdoi_attr_v(IPSECDOI_ATTR_AUTH, tr2->authtype),
    599 			s_ipsecdoi_attr_v(IPSECDOI_ATTR_AUTH, tr1->authtype));
    600 		return 1;
    601 	}
    602 
    603 	/* Check key length regarding checkmode
    604 	 * XXX Shall we send some kind of notify message when key length rejected ?
    605 	 */
    606 	switch(check_level){
    607 	case PROP_CHECK_OBEY:
    608 		return 0;
    609 		break;
    610 
    611 	case PROP_CHECK_STRICT:
    612 		/* FALLTHROUGH */
    613 	case PROP_CHECK_CLAIM:
    614 		if (tr1->encklen < tr2->encklen) {
    615 		plog(LLV_WARNING, LOCATION, NULL,
    616 				 "low key length proposed, "
    617 				 "mine:%d peer:%d.\n",
    618 			tr2->encklen, tr1->encklen);
    619 			return 1;
    620 		}
    621 		break;
    622 	case PROP_CHECK_EXACT:
    623 		if (tr1->encklen != tr2->encklen) {
    624 			plog(LLV_WARNING, LOCATION, NULL,
    625 				 "key length mismatched, "
    626 				 "mine:%d peer:%d.\n",
    627 				 tr2->encklen, tr1->encklen);
    628 			return 1;
    629 		}
    630 		break;
    631 	}
    632 
    633 	return 0;
    634 }
    635 
    636 int
    637 set_satrnsbysainfo(pr, sainfo)
    638 	struct saproto *pr;
    639 	struct sainfo *sainfo;
    640 {
    641 	struct sainfoalg *a, *b;
    642 	struct satrns *newtr;
    643 	int t;
    644 
    645 	switch (pr->proto_id) {
    646 	case IPSECDOI_PROTO_IPSEC_AH:
    647 		if (sainfo->algs[algclass_ipsec_auth] == NULL) {
    648 			plog(LLV_ERROR, LOCATION, NULL,
    649 				"no auth algorithm found\n");
    650 			goto err;
    651 		}
    652 		t = 1;
    653 		for (a = sainfo->algs[algclass_ipsec_auth]; a; a = a->next) {
    654 
    655 			if (a->alg == IPSECDOI_ATTR_AUTH_NONE)
    656 				continue;
    657 
    658 			/* allocate satrns */
    659 			newtr = newsatrns();
    660 			if (newtr == NULL) {
    661 				plog(LLV_ERROR, LOCATION, NULL,
    662 					"failed to allocate satrns.\n");
    663 				goto err;
    664 			}
    665 
    666 			newtr->trns_no = t++;
    667 			newtr->trns_id = ipsecdoi_authalg2trnsid(a->alg);
    668 			newtr->authtype = a->alg;
    669 
    670 			inssatrns(pr, newtr);
    671 		}
    672 		break;
    673 	case IPSECDOI_PROTO_IPSEC_ESP:
    674 		if (sainfo->algs[algclass_ipsec_enc] == NULL) {
    675 			plog(LLV_ERROR, LOCATION, NULL,
    676 				"no encryption algorithm found\n");
    677 			goto err;
    678 		}
    679 		t = 1;
    680 		for (a = sainfo->algs[algclass_ipsec_enc]; a; a = a->next) {
    681 			for (b = sainfo->algs[algclass_ipsec_auth]; b; b = b->next) {
    682 				/* allocate satrns */
    683 				newtr = newsatrns();
    684 				if (newtr == NULL) {
    685 					plog(LLV_ERROR, LOCATION, NULL,
    686 						"failed to allocate satrns.\n");
    687 					goto err;
    688 				}
    689 
    690 				newtr->trns_no = t++;
    691 				newtr->trns_id = a->alg;
    692 				newtr->encklen = a->encklen;
    693 				newtr->authtype = b->alg;
    694 
    695 				inssatrns(pr, newtr);
    696 			}
    697 		}
    698 		break;
    699 	case IPSECDOI_PROTO_IPCOMP:
    700 		if (sainfo->algs[algclass_ipsec_comp] == NULL) {
    701 			plog(LLV_ERROR, LOCATION, NULL,
    702 				"no ipcomp algorithm found\n");
    703 			goto err;
    704 		}
    705 		t = 1;
    706 		for (a = sainfo->algs[algclass_ipsec_comp]; a; a = a->next) {
    707 
    708 			/* allocate satrns */
    709 			newtr = newsatrns();
    710 			if (newtr == NULL) {
    711 				plog(LLV_ERROR, LOCATION, NULL,
    712 					"failed to allocate satrns.\n");
    713 				goto err;
    714 			}
    715 
    716 			newtr->trns_no = t++;
    717 			newtr->trns_id = a->alg;
    718 			newtr->authtype = IPSECDOI_ATTR_AUTH_NONE; /*no auth*/
    719 
    720 			inssatrns(pr, newtr);
    721 		}
    722 		break;
    723 	default:
    724 		plog(LLV_ERROR, LOCATION, NULL,
    725 			"unknown proto_id (%d).\n", pr->proto_id);
    726 		goto err;
    727 	}
    728 
    729 	/* no proposal found */
    730 	if (pr->head == NULL) {
    731 		plog(LLV_ERROR, LOCATION, NULL, "no algorithms found.\n");
    732 		return -1;
    733 	}
    734 
    735 	return 0;
    736 
    737 err:
    738 	flushsatrns(pr->head);
    739 	return -1;
    740 }
    741 
    742 struct saprop *
    743 aproppair2saprop(p0)
    744 	struct prop_pair *p0;
    745 {
    746 	struct prop_pair *p, *t;
    747 	struct saprop *newpp;
    748 	struct saproto *newpr;
    749 	struct satrns *newtr;
    750 	u_int8_t *spi;
    751 
    752 	if (p0 == NULL)
    753 		return NULL;
    754 
    755 	/* allocate ipsec a sa proposal */
    756 	newpp = newsaprop();
    757 	if (newpp == NULL) {
    758 		plog(LLV_ERROR, LOCATION, NULL,
    759 			"failed to allocate saprop.\n");
    760 		return NULL;
    761 	}
    762 	newpp->prop_no = p0->prop->p_no;
    763 	/* lifetime & lifebyte must be updated later */
    764 
    765 	for (p = p0; p; p = p->next) {
    766 
    767 		/* allocate ipsec sa protocol */
    768 		newpr = newsaproto();
    769 		if (newpr == NULL) {
    770 			plog(LLV_ERROR, LOCATION, NULL,
    771 				"failed to allocate saproto.\n");
    772 			goto err;
    773 		}
    774 
    775 		/* check spi size */
    776 		/* XXX should be handled isakmp cookie */
    777 		if (sizeof(newpr->spi) < p->prop->spi_size) {
    778 			plog(LLV_ERROR, LOCATION, NULL,
    779 				"invalid spi size %d.\n", p->prop->spi_size);
    780 			racoon_free(newpr);
    781 			goto err;
    782 		}
    783 
    784 		/*
    785 		 * XXX SPI bits are left-filled, for use with IPComp.
    786 		 * we should be switching to variable-length spi field...
    787 		 */
    788 		newpr->proto_id = p->prop->proto_id;
    789 		newpr->spisize = p->prop->spi_size;
    790 		memset(&newpr->spi, 0, sizeof(newpr->spi));
    791 		spi = (u_int8_t *)&newpr->spi;
    792 		spi += sizeof(newpr->spi);
    793 		spi -= p->prop->spi_size;
    794 		memcpy(spi, p->prop + 1, p->prop->spi_size);
    795 		newpr->reqid_in = 0;
    796 		newpr->reqid_out = 0;
    797 
    798 		for (t = p; t; t = t->tnext) {
    799 
    800 			plog(LLV_DEBUG, LOCATION, NULL,
    801 				"prop#=%d prot-id=%s spi-size=%d "
    802 				"#trns=%d trns#=%d trns-id=%s\n",
    803 				t->prop->p_no,
    804 				s_ipsecdoi_proto(t->prop->proto_id),
    805 				t->prop->spi_size, t->prop->num_t,
    806 				t->trns->t_no,
    807 				s_ipsecdoi_trns(t->prop->proto_id,
    808 				t->trns->t_id));
    809 
    810 			/* allocate ipsec sa transform */
    811 			newtr = newsatrns();
    812 			if (newtr == NULL) {
    813 				plog(LLV_ERROR, LOCATION, NULL,
    814 					"failed to allocate satrns.\n");
    815 				racoon_free(newpr);
    816 				goto err;
    817 			}
    818 
    819 			if (ipsecdoi_t2satrns(t->trns,
    820 			    newpp, newpr, newtr) < 0) {
    821 				flushsaprop(newpp);
    822 				racoon_free(newtr);
    823 				racoon_free(newpr);
    824 				return NULL;
    825 			}
    826 
    827 			inssatrns(newpr, newtr);
    828 		}
    829 
    830 		/*
    831 		 * If the peer does not specify encryption mode, use
    832 		 * transport mode by default.  This is to conform to
    833 		 * draft-shacham-ippcp-rfc2393bis-08.txt (explicitly specifies
    834 		 * that unspecified == transport), as well as RFC2407
    835 		 * (unspecified == implementation dependent default).
    836 		 */
    837 		if (newpr->encmode == 0)
    838 			newpr->encmode = IPSECDOI_ATTR_ENC_MODE_TRNS;
    839 
    840 		inssaproto(newpp, newpr);
    841 	}
    842 
    843 	return newpp;
    844 
    845 err:
    846 	flushsaprop(newpp);
    847 	return NULL;
    848 }
    849 
    850 void
    851 flushsaprop(head)
    852 	struct saprop *head;
    853 {
    854 	struct saprop *p, *save;
    855 
    856 	for (p = head; p != NULL; p = save) {
    857 		save = p->next;
    858 		flushsaproto(p->head);
    859 		racoon_free(p);
    860 	}
    861 
    862 	return;
    863 }
    864 
    865 void
    866 flushsaproto(head)
    867 	struct saproto *head;
    868 {
    869 	struct saproto *p, *save;
    870 
    871 	for (p = head; p != NULL; p = save) {
    872 		save = p->next;
    873 		flushsatrns(p->head);
    874 		vfree(p->keymat);
    875 		vfree(p->keymat_p);
    876 		racoon_free(p);
    877 	}
    878 
    879 	return;
    880 }
    881 
    882 void
    883 flushsatrns(head)
    884 	struct satrns *head;
    885 {
    886 	struct satrns *p, *save;
    887 
    888 	for (p = head; p != NULL; p = save) {
    889 		save = p->next;
    890 		racoon_free(p);
    891 	}
    892 
    893 	return;
    894 }
    895 
    896 /*
    897  * print multiple proposals
    898  */
    899 void
    900 printsaprop(pri, pp)
    901 	const int pri;
    902 	const struct saprop *pp;
    903 {
    904 	const struct saprop *p;
    905 
    906 	if (pp == NULL) {
    907 		plog(pri, LOCATION, NULL, "(null)");
    908 		return;
    909 	}
    910 
    911 	for (p = pp; p; p = p->next) {
    912 		printsaprop0(pri, p);
    913 	}
    914 
    915 	return;
    916 }
    917 
    918 /*
    919  * print one proposal.
    920  */
    921 void
    922 printsaprop0(pri, pp)
    923 	int pri;
    924 	const struct saprop *pp;
    925 {
    926 	const struct saproto *p;
    927 
    928 	if (pp == NULL)
    929 		return;
    930 
    931 	for (p = pp->head; p; p = p->next) {
    932 		printsaproto(pri, p);
    933 	}
    934 
    935 	return;
    936 }
    937 
    938 void
    939 printsaproto(pri, pr)
    940 	const int pri;
    941 	const struct saproto *pr;
    942 {
    943 	struct satrns *tr;
    944 
    945 	if (pr == NULL)
    946 		return;
    947 
    948 	plog(pri, LOCATION, NULL,
    949 		" (proto_id=%s spisize=%d spi=%08lx spi_p=%08lx "
    950 		"encmode=%s reqid=%d:%d)\n",
    951 		s_ipsecdoi_proto(pr->proto_id),
    952 		(int)pr->spisize,
    953 		(unsigned long)ntohl(pr->spi),
    954 		(unsigned long)ntohl(pr->spi_p),
    955 		s_ipsecdoi_attr_v(IPSECDOI_ATTR_ENC_MODE, pr->encmode),
    956 		(int)pr->reqid_in, (int)pr->reqid_out);
    957 
    958 	for (tr = pr->head; tr; tr = tr->next) {
    959 		printsatrns(pri, pr->proto_id, tr);
    960 	}
    961 
    962 	return;
    963 }
    964 
    965 void
    966 printsatrns(pri, proto_id, tr)
    967 	const int pri;
    968 	const int proto_id;
    969 	const struct satrns *tr;
    970 {
    971 	if (tr == NULL)
    972 		return;
    973 
    974 	switch (proto_id) {
    975 	case IPSECDOI_PROTO_IPSEC_AH:
    976 		plog(pri, LOCATION, NULL,
    977 			"  (trns_id=%s authtype=%s)\n",
    978 			s_ipsecdoi_trns(proto_id, tr->trns_id),
    979 			s_ipsecdoi_attr_v(IPSECDOI_ATTR_AUTH, tr->authtype));
    980 		break;
    981 	case IPSECDOI_PROTO_IPSEC_ESP:
    982 		plog(pri, LOCATION, NULL,
    983 			"  (trns_id=%s encklen=%d authtype=%s)\n",
    984 			s_ipsecdoi_trns(proto_id, tr->trns_id),
    985 			tr->encklen,
    986 			s_ipsecdoi_attr_v(IPSECDOI_ATTR_AUTH, tr->authtype));
    987 		break;
    988 	case IPSECDOI_PROTO_IPCOMP:
    989 		plog(pri, LOCATION, NULL,
    990 			"  (trns_id=%s)\n",
    991 			s_ipsecdoi_trns(proto_id, tr->trns_id));
    992 		break;
    993 	default:
    994 		plog(pri, LOCATION, NULL,
    995 			"(unknown proto_id %d)\n", proto_id);
    996 	}
    997 
    998 	return;
    999 }
   1000 
   1001 void
   1002 print_proppair0(pri, p, level)
   1003 	int pri;
   1004 	struct prop_pair *p;
   1005 	int level;
   1006 {
   1007 	char spc[21];
   1008 
   1009 	memset(spc, ' ', sizeof(spc));
   1010 	spc[sizeof(spc) - 1] = '\0';
   1011 	if (level < 20) {
   1012 		spc[level] = '\0';
   1013 	}
   1014 
   1015 	plog(pri, LOCATION, NULL,
   1016 		"%s%p: next=%p tnext=%p\n", spc, p, p->next, p->tnext);
   1017 	if (p->next)
   1018 		print_proppair0(pri, p->next, level + 1);
   1019 	if (p->tnext)
   1020 		print_proppair0(pri, p->tnext, level + 1);
   1021 }
   1022 
   1023 void
   1024 print_proppair(pri, p)
   1025 	int pri;
   1026 	struct prop_pair *p;
   1027 {
   1028 	print_proppair0(pri, p, 1);
   1029 }
   1030 
   1031 int
   1032 set_proposal_from_policy(iph2, sp_main, sp_sub)
   1033 	struct ph2handle *iph2;
   1034 	struct secpolicy *sp_main, *sp_sub;
   1035 {
   1036 	struct saprop *newpp;
   1037 	struct ipsecrequest *req;
   1038 	int encmodesv = IPSECDOI_ATTR_ENC_MODE_TRNS; /* use only when complex_bundle */
   1039 
   1040 	newpp = newsaprop();
   1041 	if (newpp == NULL) {
   1042 		plog(LLV_ERROR, LOCATION, NULL,
   1043 			"failed to allocate saprop.\n");
   1044 		goto err;
   1045 	}
   1046 	newpp->prop_no = 1;
   1047 	newpp->lifetime = iph2->sainfo->lifetime;
   1048 	newpp->lifebyte = iph2->sainfo->lifebyte;
   1049 	newpp->pfs_group = iph2->sainfo->pfs_group;
   1050 
   1051 	if (lcconf->complex_bundle)
   1052 		goto skip1;
   1053 
   1054 	/*
   1055 	 * decide the encryption mode of this SA bundle.
   1056 	 * the mode becomes tunnel mode when there is even one policy
   1057 	 * of tunnel mode in the SPD.  otherwise the mode becomes
   1058 	 * transport mode.
   1059 	 */
   1060 	for (req = sp_main->req; req; req = req->next) {
   1061 		if (req->saidx.mode == IPSEC_MODE_TUNNEL) {
   1062 			encmodesv = pfkey2ipsecdoi_mode(req->saidx.mode);
   1063 #ifdef ENABLE_NATT
   1064 			if (iph2->ph1 && (iph2->ph1->natt_flags & NAT_DETECTED))
   1065 				encmodesv += iph2->ph1->natt_options->mode_udp_diff;
   1066 #endif
   1067 			break;
   1068 		}
   1069 	}
   1070 
   1071     skip1:
   1072 	for (req = sp_main->req; req; req = req->next) {
   1073 		struct saproto *newpr;
   1074 		caddr_t paddr = NULL;
   1075 
   1076 		/*
   1077 		 * check if SA bundle ?
   1078 		 * nested SAs negotiation is NOT supported.
   1079 		 *       me +--- SA1 ---+ peer1
   1080 		 *       me +--- SA2 --------------+ peer2
   1081 		 */
   1082 #ifdef __linux__
   1083 		if (req->saidx.src.ss_family && req->saidx.dst.ss_family) {
   1084 #else
   1085 		if (req->saidx.src.ss_len && req->saidx.dst.ss_len) {
   1086 #endif
   1087 			/* check the end of ip addresses of SA */
   1088 			if (iph2->side == INITIATOR)
   1089 				paddr = (caddr_t)&req->saidx.dst;
   1090 			else
   1091 				paddr = (caddr_t)&req->saidx.src;
   1092 		}
   1093 
   1094 		/* allocate ipsec sa protocol */
   1095 		newpr = newsaproto();
   1096 		if (newpr == NULL) {
   1097 			plog(LLV_ERROR, LOCATION, NULL,
   1098 				"failed to allocate saproto.\n");
   1099 			goto err;
   1100 		}
   1101 
   1102 		newpr->proto_id = ipproto2doi(req->saidx.proto);
   1103 		if (newpr->proto_id == IPSECDOI_PROTO_IPCOMP)
   1104 			newpr->spisize = 2;
   1105 		else
   1106 			newpr->spisize = 4;
   1107 		if (lcconf->complex_bundle) {
   1108 			newpr->encmode = pfkey2ipsecdoi_mode(req->saidx.mode);
   1109 #ifdef ENABLE_NATT
   1110 			if (iph2->ph1 && (iph2->ph1->natt_flags & NAT_DETECTED))
   1111 				newpr->encmode +=
   1112 				    iph2->ph1->natt_options->mode_udp_diff;
   1113 #endif
   1114 		}
   1115 		else
   1116 			newpr->encmode = encmodesv;
   1117 
   1118 		if (iph2->side == INITIATOR)
   1119 			newpr->reqid_out = req->saidx.reqid;
   1120 		else
   1121 			newpr->reqid_in = req->saidx.reqid;
   1122 
   1123 		if (set_satrnsbysainfo(newpr, iph2->sainfo) < 0) {
   1124 			plog(LLV_ERROR, LOCATION, NULL,
   1125 				"failed to get algorithms.\n");
   1126 			racoon_free(newpr);
   1127 			goto err;
   1128 		}
   1129 
   1130 		/* set new saproto */
   1131 		inssaprotorev(newpp, newpr);
   1132 	}
   1133 
   1134 	/* get reqid_in from inbound policy */
   1135 	if (sp_sub) {
   1136 		struct saproto *pr;
   1137 
   1138 		req = sp_sub->req;
   1139 		pr = newpp->head;
   1140 		while (req && pr) {
   1141 			if (iph2->side == INITIATOR)
   1142 				pr->reqid_in = req->saidx.reqid;
   1143 			else
   1144 				pr->reqid_out = req->saidx.reqid;
   1145 			pr = pr->next;
   1146 			req = req->next;
   1147 		}
   1148 		if (pr || req) {
   1149 			plog(LLV_NOTIFY, LOCATION, NULL,
   1150 				"There is a difference "
   1151 				"between the in/out bound policies in SPD.\n");
   1152 		}
   1153 	}
   1154 
   1155 	iph2->proposal = newpp;
   1156 
   1157 	printsaprop0(LLV_DEBUG, newpp);
   1158 
   1159 	return 0;
   1160 err:
   1161 	flushsaprop(newpp);
   1162 	return -1;
   1163 }
   1164 
   1165 /*
   1166  * generate a policy from peer's proposal.
   1167  * this function unconditionally choices first proposal in SA payload
   1168  * passed by peer.
   1169  */
   1170 int
   1171 set_proposal_from_proposal(iph2)
   1172 	struct ph2handle *iph2;
   1173 {
   1174         struct saprop *newpp = NULL, *pp0, *pp_peer = NULL;
   1175 	struct saproto *newpr = NULL, *pr;
   1176 	struct prop_pair **pair;
   1177 	int error = -1;
   1178 	int i;
   1179 
   1180 	/* get proposal pair */
   1181 	pair = get_proppair(iph2->sa, IPSECDOI_TYPE_PH2);
   1182 	if (pair == NULL)
   1183 		goto end;
   1184 
   1185 	/*
   1186 	 * make my proposal according as the client proposal.
   1187 	 * XXX assumed there is only one proposal even if it's the SA bundle.
   1188 	 */
   1189         for (i = 0; i < MAXPROPPAIRLEN; i++) {
   1190                 if (pair[i] == NULL)
   1191                         continue;
   1192 
   1193 		if (pp_peer != NULL)
   1194 			flushsaprop(pp_peer);
   1195 
   1196 		pp_peer = aproppair2saprop(pair[i]);
   1197 		if (pp_peer == NULL)
   1198 			goto end;
   1199 
   1200 		pp0 = newsaprop();
   1201 		if (pp0 == NULL) {
   1202 			plog(LLV_ERROR, LOCATION, NULL,
   1203 				"failed to allocate saprop.\n");
   1204 			goto end;
   1205 		}
   1206 		pp0->prop_no = 1;
   1207 		pp0->lifetime = iph2->sainfo->lifetime;
   1208 		pp0->lifebyte = iph2->sainfo->lifebyte;
   1209 		pp0->pfs_group = iph2->sainfo->pfs_group;
   1210 
   1211 #ifdef HAVE_SECCTX
   1212 		if (*pp_peer->sctx.ctx_str) {
   1213 			pp0->sctx.ctx_doi = pp_peer->sctx.ctx_doi;
   1214 			pp0->sctx.ctx_alg = pp_peer->sctx.ctx_alg;
   1215 			pp0->sctx.ctx_strlen = pp_peer->sctx.ctx_strlen;
   1216 			memcpy(pp0->sctx.ctx_str, pp_peer->sctx.ctx_str,
   1217 			       pp_peer->sctx.ctx_strlen);
   1218 		}
   1219 #endif /* HAVE_SECCTX */
   1220 
   1221 		if (pp_peer->next != NULL) {
   1222 			plog(LLV_ERROR, LOCATION, NULL,
   1223 				"pp_peer is inconsistency, ignore it.\n");
   1224 			/*FALLTHROUGH*/
   1225 		}
   1226 
   1227 		for (pr = pp_peer->head; pr; pr = pr->next)
   1228 		{
   1229 			struct remoteconf *conf;
   1230 
   1231 			newpr = newsaproto();
   1232 			if (newpr == NULL)
   1233 			{
   1234 				plog(LLV_ERROR, LOCATION, NULL,
   1235 					"failed to allocate saproto.\n");
   1236 				racoon_free(pp0);
   1237 				goto end;
   1238 			}
   1239 			newpr->proto_id = pr->proto_id;
   1240 			newpr->spisize = pr->spisize;
   1241 			newpr->encmode = pr->encmode;
   1242 			newpr->spi = 0;
   1243 			newpr->spi_p = pr->spi;     /* copy peer's SPI */
   1244 			newpr->reqid_in = 0;
   1245 			newpr->reqid_out = 0;
   1246 
   1247 			conf = getrmconf(iph2->dst);
   1248 			if (conf != NULL &&
   1249 				conf->gen_policy == GENERATE_POLICY_UNIQUE){
   1250 				newpr->reqid_in = g_nextreqid ;
   1251 				newpr->reqid_out = g_nextreqid ++;
   1252 				/*
   1253 				 * XXX there is a (very limited)
   1254 				 * risk of reusing the same reqid
   1255 				 * as another SP entry for the same peer
   1256 				 */
   1257 				if(g_nextreqid >= IPSEC_MANUAL_REQID_MAX)
   1258 					g_nextreqid = 1;
   1259 			}else{
   1260 				newpr->reqid_in = 0;
   1261 				newpr->reqid_out = 0;
   1262 			}
   1263 
   1264 			if (set_satrnsbysainfo(newpr, iph2->sainfo) < 0)
   1265 			{
   1266 				plog(LLV_ERROR, LOCATION, NULL,
   1267 					"failed to get algorithms.\n");
   1268 				racoon_free(newpr);
   1269 				racoon_free(pp0);
   1270 				goto end;
   1271 			}
   1272 			inssaproto(pp0, newpr);
   1273 		}
   1274 
   1275 		inssaprop(&newpp, pp0);
   1276         }
   1277 
   1278 	plog(LLV_DEBUG, LOCATION, NULL, "make a proposal from peer's:\n");
   1279 	printsaprop0(LLV_DEBUG, newpp);
   1280 
   1281 	iph2->proposal = newpp;
   1282 
   1283 	error = 0;
   1284 
   1285 end:
   1286 	if (error && newpp)
   1287 		flushsaprop(newpp);
   1288 
   1289 	if (pp_peer)
   1290 		flushsaprop(pp_peer);
   1291 	if (pair)
   1292 		free_proppair(pair);
   1293 	return error;
   1294 }
   1295