Home | History | Annotate | Line # | Download | only in racoon
      1 /*	$NetBSD: remoteconf.c,v 1.32 2025/03/08 16:39:08 christos Exp $	*/
      2 
      3 /* Id: remoteconf.c,v 1.38 2006/05/06 15:52:44 manubsd 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/types.h>
     37 #include <sys/param.h>
     38 #include <sys/socket.h>
     39 #include <sys/queue.h>
     40 
     41 #include <netinet/in.h>
     42 #include <netinet/in_systm.h>
     43 #include <netinet/ip.h>
     44 
     45 #include PATH_IPSEC_H
     46 
     47 #include <stdlib.h>
     48 #include <stdio.h>
     49 #include <string.h>
     50 #include <errno.h>
     51 
     52 #include "var.h"
     53 #include "misc.h"
     54 #include "vmbuf.h"
     55 #include "plog.h"
     56 #include "sockmisc.h"
     57 #include "genlist.h"
     58 #include "debug.h"
     59 
     60 #include "isakmp_var.h"
     61 #ifdef ENABLE_HYBRID
     62 #include "isakmp_xauth.h"
     63 #endif
     64 #include "isakmp.h"
     65 #include "ipsec_doi.h"
     66 #include "crypto_openssl.h"
     67 #include "oakley.h"
     68 #include "remoteconf.h"
     69 #include "localconf.h"
     70 #include "grabmyaddr.h"
     71 #include "policy.h"
     72 #include "proposal.h"
     73 #include "vendorid.h"
     74 #include "gcmalloc.h"
     75 #include "strnames.h"
     76 #include "algorithm.h"
     77 #include "nattraversal.h"
     78 #include "isakmp_frag.h"
     79 #include "handler.h"
     80 #include "genlist.h"
     81 #include "rsalist.h"
     82 
     83 typedef TAILQ_HEAD(_rmtree, remoteconf) remoteconf_tailq_head_t;
     84 static remoteconf_tailq_head_t rmtree, rmtree_save;
     85 
     86 /*
     87  * Script hook names and script hook paths
     88  */
     89 const char *script_names[SCRIPT_MAX + 1] = {
     90 	"phase1_up", "phase1_down", "phase1_dead" };
     91 
     92 /*%%%*/
     93 
     94 int
     95 rmconf_match_identity(struct remoteconf *rmconf, vchar_t *id_p)
     96 {
     97 	struct ipsecdoi_id_b *id_b = (struct ipsecdoi_id_b *) id_p->v;
     98 	struct sockaddr *sa;
     99 	caddr_t sa1, sa2;
    100 	vchar_t ident;
    101 	struct idspec *id;
    102 	struct genlist_entry *gpb;
    103 
    104 	/* compare with the ID if specified. */
    105 	if (!genlist_next(rmconf->idvl_p, 0))
    106 		return 0;
    107 
    108 	for (id = genlist_next(rmconf->idvl_p, &gpb); id; id = genlist_next(0, &gpb)) {
    109 		/* No ID specified in configuration, so it is ok */
    110 		if (id->id == 0)
    111 			return 0;
    112 
    113 		/* check the type of both IDs */
    114 		if (id->idtype != doi2idtype(id_b->type))
    115 			continue;  /* ID type mismatch */
    116 
    117 		/* compare defined ID with the ID sent by peer. */
    118 		switch (id->idtype) {
    119 		case IDTYPE_ASN1DN:
    120 			ident.v = id_p->v + sizeof(*id_b);
    121 			ident.l = id_p->l - sizeof(*id_b);
    122 			if (eay_cmp_asn1dn(id->id, &ident) == 0)
    123 				return 0;
    124 			break;
    125 		case IDTYPE_ADDRESS:
    126 			sa = (struct sockaddr *)id->id->v;
    127 			sa2 = (caddr_t)(id_b + 1);
    128 			switch (sa->sa_family) {
    129 			case AF_INET:
    130 				if (id_p->l - sizeof(*id_b) != sizeof(struct in_addr))
    131 					continue;  /* ID value mismatch */
    132 				sa1 = (caddr_t) &((struct sockaddr_in *)sa)->sin_addr;
    133 				if (memcmp(sa1, sa2, sizeof(struct in_addr)) == 0)
    134 					return 0;
    135 				break;
    136 #ifdef INET6
    137 			case AF_INET6:
    138 				if (id_p->l - sizeof(*id_b) != sizeof(struct in6_addr))
    139 					continue;  /* ID value mismatch */
    140 				sa1 = (caddr_t) &((struct sockaddr_in6 *)sa)->sin6_addr;
    141 				if (memcmp(sa1, sa2, sizeof(struct in6_addr)) == 0)
    142 					return 0;
    143 				break;
    144 #endif
    145 			default:
    146 				break;
    147 			}
    148 			break;
    149 		default:
    150 			if (memcmp(id->id->v, id_b + 1, id->id->l) == 0)
    151 				return 0;
    152 			break;
    153 		}
    154 	}
    155 
    156 	plog(LLV_WARNING, LOCATION, NULL, "No ID match.\n");
    157 	if (rmconf->verify_identifier)
    158 		return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
    159 
    160 	return 0;
    161 }
    162 
    163 static int
    164 rmconf_match_etype_and_approval(struct remoteconf *rmconf, int etype,
    165     struct isakmpsa *approval)
    166 {
    167 	if (check_etypeok(rmconf, (void *) (intptr_t) etype) == 0)
    168 		return ISAKMP_NTYPE_NO_PROPOSAL_CHOSEN;
    169 
    170 	if (approval == NULL)
    171 		return 0;
    172 
    173 	if (etype == ISAKMP_ETYPE_AGG &&
    174 	    approval->dh_group != rmconf->dh_group)
    175 		return ISAKMP_NTYPE_NO_PROPOSAL_CHOSEN;
    176 
    177 	if (checkisakmpsa(rmconf->pcheck_level, approval,
    178 			  rmconf->proposal) == NULL)
    179 		return ISAKMP_NTYPE_NO_PROPOSAL_CHOSEN;
    180 
    181 	return 0;
    182 }
    183 
    184 enum rmconf_match_t {
    185 	MATCH_NONE		= 0,
    186 	MATCH_BASIC		= 0x0000001,
    187 	MATCH_ADDRESS		= 0x0000002,
    188 	MATCH_SA		= 0x0000004,
    189 	MATCH_IDENTITY		= 0x0000008,
    190 	MATCH_AUTH_IDENTITY	= 0x0000010,
    191 };
    192 
    193 static int
    194 rmconf_match_type(struct rmconfselector *rmsel, struct remoteconf *rmconf)
    195 {
    196 	int ret = MATCH_NONE, tmp;
    197 
    198 	/* No match at all: unwanted anonymous */
    199 	if ((rmsel->flags & GETRMCONF_F_NO_ANONYMOUS) &&
    200 	    rmconf->remote->sa_family == AF_UNSPEC){
    201 		plog(LLV_DEBUG2, LOCATION, rmsel->remote,
    202 		     "Not matched: Anonymous conf.\n");
    203 		return MATCH_NONE;
    204 	}
    205 
    206 	if ((rmsel->flags & GETRMCONF_F_NO_PASSIVE) && rmconf->passive){
    207 		plog(LLV_DEBUG2, LOCATION, rmsel->remote,
    208 		     "Not matched: passive conf.\n");
    209 		return MATCH_NONE;
    210 	}
    211 
    212 	ret |= MATCH_BASIC;
    213 
    214 	/* Check address */
    215 	if (rmsel->remote != NULL) {
    216 		if (rmconf->remote->sa_family != AF_UNSPEC) {
    217 			if (cmpsaddr(rmsel->remote, rmconf->remote) == CMPSADDR_MISMATCH){
    218 				plog(LLV_DEBUG2, LOCATION, rmsel->remote,
    219 				     "Not matched: address mismatch.\n");
    220 				return MATCH_NONE;
    221 			}
    222 
    223 			/* Address matched */
    224 			ret |= MATCH_ADDRESS;
    225 		}
    226 	}
    227 
    228 	/* Check etype and approval */
    229 	if (rmsel->etype != ISAKMP_ETYPE_NONE) {
    230 		tmp=rmconf_match_etype_and_approval(rmconf, rmsel->etype,
    231 						    rmsel->approval);
    232 		if (tmp != 0){
    233 			plog(LLV_DEBUG2, LOCATION, rmsel->remote,
    234 			     "Not matched: etype (%d)/approval mismatch (%d).\n", rmsel->etype, tmp);
    235 			return MATCH_NONE;
    236 		}
    237 		ret |= MATCH_SA;
    238 	}
    239 
    240 	/* Check identity */
    241 	if (rmsel->identity != NULL && rmconf->verify_identifier) {
    242 		if (rmconf_match_identity(rmconf, rmsel->identity) != 0){
    243 			plog(LLV_DEBUG2, LOCATION, rmsel->remote,
    244 			     "Not matched: identity mismatch.\n");
    245 			return MATCH_NONE;
    246 		}
    247 		ret |= MATCH_IDENTITY;
    248 	}
    249 
    250 	/* Check certificate request */
    251 	if (rmsel->certificate_request != NULL) {
    252 		if (oakley_get_certtype(rmsel->certificate_request) !=
    253 		    oakley_get_certtype(rmconf->mycert)){
    254 			plog(LLV_DEBUG2, LOCATION, rmsel->remote,
    255 			     "Not matched: cert type mismatch.\n");
    256 			return MATCH_NONE;
    257 		}
    258 
    259 		if (rmsel->certificate_request->l > 1) {
    260 			vchar_t *issuer;
    261 
    262 			issuer = eay_get_x509asn1issuername(rmconf->mycert);
    263 			if (rmsel->certificate_request->l - 1 != issuer->l ||
    264 			    memcmp(rmsel->certificate_request->v + 1,
    265 				   issuer->v, issuer->l) != 0) {
    266 				vfree(issuer);
    267 				plog(LLV_DEBUG2, LOCATION, rmsel->remote,
    268 				     "Not matched: cert issuer mismatch.\n");
    269 				return MATCH_NONE;
    270 			}
    271 			vfree(issuer);
    272 		} else {
    273 			if (!rmconf->match_empty_cr){
    274 				plog(LLV_DEBUG2, LOCATION, rmsel->remote,
    275 				     "Not matched: empty certificate request.\n");
    276 				return MATCH_NONE;
    277 			}
    278 		}
    279 
    280 		ret |= MATCH_AUTH_IDENTITY;
    281 	}
    282 
    283 	return ret;
    284 }
    285 
    286 void
    287 rmconf_selector_from_ph1(struct rmconfselector *rmsel, struct ph1handle *iph1)
    288 {
    289 	memset(rmsel, 0, sizeof(*rmsel));
    290 	rmsel->flags = 0;
    291 	rmsel->remote = iph1->remote;
    292 	rmsel->etype = iph1->etype;
    293 	rmsel->approval = iph1->approval;
    294 	rmsel->identity = iph1->id_p;
    295 	rmsel->certificate_request = iph1->cr_p;
    296 }
    297 
    298 int
    299 enumrmconf(struct rmconfselector *rmsel,
    300     int (*enum_func)(struct remoteconf *, void *), void *enum_arg)
    301 {
    302 	struct remoteconf *p;
    303 	int ret = 0;
    304 
    305 	RACOON_TAILQ_FOREACH_REVERSE(p, &rmtree, _rmtree, chain) {
    306 		plog(LLV_DEBUG2, LOCATION, rmsel->remote,
    307 		     "Checking remote conf \"%s\" %s.\n", p->name,
    308 		     p->remote->sa_family == AF_UNSPEC ?
    309 		     "anonymous" : saddr2str(p->remote));
    310 
    311 		if (rmsel != NULL) {
    312 			if (rmconf_match_type(rmsel, p) == MATCH_NONE){
    313 				plog(LLV_DEBUG2, LOCATION, rmsel->remote,
    314 				     "Not matched.\n");
    315 				continue;
    316 			}
    317 		}
    318 
    319 		plog(LLV_DEBUG2, LOCATION, NULL,
    320 		     "enumrmconf: \"%s\" matches.\n", p->name);
    321 
    322 		ret = (*enum_func)(p, enum_arg);
    323 		if (ret)
    324 			break;
    325 	}
    326 
    327 	return ret;
    328 }
    329 
    330 struct rmconf_find_context {
    331 	struct rmconfselector sel;
    332 
    333 	struct remoteconf *rmconf;
    334 	int match_type;
    335 	int num_found;
    336 };
    337 
    338 static int
    339 rmconf_find(struct remoteconf *rmconf, void *ctx)
    340 {
    341 	struct rmconf_find_context *fctx = (struct rmconf_find_context *) ctx;
    342 	int match_type;
    343 
    344 	/* First matching remote conf? */
    345 	match_type = rmconf_match_type(&fctx->sel, rmconf);
    346 
    347 	if (fctx->rmconf != NULL) {
    348 		/* More ambiguous matches are ignored. */
    349 		if (match_type < fctx->match_type)
    350 			return 0;
    351 
    352 		if (match_type == fctx->match_type) {
    353 			/* Ambiguous match */
    354 			fctx->num_found++;
    355 			return 0;
    356 		}
    357 	}
    358 
    359 	/* More exact match found */
    360 	fctx->match_type = match_type;
    361 	fctx->num_found = 1;
    362 	fctx->rmconf = rmconf;
    363 
    364 	return 0;
    365 }
    366 
    367 /*
    368  * search remote configuration.
    369  * don't use port number to search if its value is either IPSEC_PORT_ANY.
    370  * If matching anonymous entry, then new entry is copied from anonymous entry.
    371  * If no anonymous entry found, then return NULL.
    372  * OUT:	NULL:	NG
    373  *	Other:	remote configuration entry.
    374  */
    375 
    376 struct remoteconf *
    377 getrmconf(struct sockaddr *remote, int flags)
    378 {
    379 	struct rmconf_find_context ctx;
    380 
    381 	memset(&ctx, 0, sizeof(ctx));
    382 	ctx.sel.flags = flags;
    383 	ctx.sel.remote = remote;
    384 
    385 	if (enumrmconf(&ctx.sel, rmconf_find, &ctx) != 0) {
    386 		plog(LLV_ERROR, LOCATION, remote,
    387 		     "multiple exact configurations.\n");
    388 		return NULL;
    389 	}
    390 
    391 	if (ctx.rmconf == NULL) {
    392 		plog(LLV_DEBUG, LOCATION, remote,
    393 		     "no remote configuration found.\n");
    394 		return NULL;
    395 	}
    396 
    397 	if (ctx.num_found != 1) {
    398 		plog(LLV_DEBUG, LOCATION, remote,
    399 		     "multiple non-exact configurations found.\n");
    400 		return NULL;
    401 	}
    402 
    403 	plog(LLV_DEBUG, LOCATION, remote,
    404 	     "configuration \"%s\" selected.\n",
    405 	     ctx.rmconf->name);
    406 
    407 	return ctx.rmconf;
    408 }
    409 
    410 struct remoteconf *
    411 getrmconf_by_ph1(struct ph1handle *iph1)
    412 {
    413 	struct rmconf_find_context ctx;
    414 
    415 	memset(&ctx, 0, sizeof(ctx));
    416 	rmconf_selector_from_ph1(&ctx.sel, iph1);
    417 	if (loglevel >= LLV_DEBUG) {
    418 		char *idstr = NULL;
    419 
    420 		if (iph1->id_p != NULL)
    421 			idstr = ipsecdoi_id2str(iph1->id_p);
    422 
    423 		plog(LLV_DEBUG, LOCATION, iph1->remote,
    424 			"getrmconf_by_ph1: remote %s, identity %s.\n",
    425 			saddr2str(iph1->remote), idstr ? idstr : "<any>");
    426 
    427 		if (idstr)
    428 			racoon_free(idstr);
    429 	}
    430 
    431 	if (enumrmconf(&ctx.sel, rmconf_find, &ctx) != 0) {
    432 		plog(LLV_ERROR, LOCATION, iph1->remote,
    433 		     "multiple exact configurations.\n");
    434 		return RMCONF_ERR_MULTIPLE;
    435 	}
    436 
    437 	if (ctx.rmconf == NULL) {
    438 		plog(LLV_DEBUG, LOCATION, iph1->remote,
    439 		     "no remote configuration found\n");
    440 		return NULL;
    441 	}
    442 
    443 	if (ctx.num_found != 1) {
    444 		plog(LLV_DEBUG, LOCATION, iph1->remote,
    445 		     "multiple non-exact configurations found.\n");
    446 		return RMCONF_ERR_MULTIPLE;
    447 	}
    448 
    449 	plog(LLV_DEBUG, LOCATION, iph1->remote,
    450 	     "configuration \"%s\" selected.\n",
    451 	     ctx.rmconf->name);
    452 
    453 	return ctx.rmconf;
    454 }
    455 
    456 struct remoteconf *
    457 getrmconf_by_name(const char *name)
    458 {
    459 	struct remoteconf *p;
    460 
    461 	plog(LLV_DEBUG, LOCATION, NULL,
    462 	     "getrmconf_by_name: remote \"%s\".\n",
    463 	     name);
    464 
    465 	RACOON_TAILQ_FOREACH_REVERSE(p, &rmtree, _rmtree, chain) {
    466 		if (p->name == NULL)
    467 			continue;
    468 
    469 		if (strcmp(name, p->name) == 0)
    470 			return p;
    471 	}
    472 
    473 	return NULL;
    474 }
    475 
    476 struct remoteconf *
    477 newrmconf(void)
    478 {
    479 	struct remoteconf *new;
    480 	int i;
    481 
    482 	new = racoon_calloc(1, sizeof(*new));
    483 	if (new == NULL)
    484 		return NULL;
    485 
    486 	new->proposal = NULL;
    487 
    488 	/* set default */
    489 	new->doitype = IPSEC_DOI;
    490 	new->sittype = IPSECDOI_SIT_IDENTITY_ONLY;
    491 	new->idvtype = IDTYPE_UNDEFINED;
    492 	new->idvl_p = genlist_init();
    493 	new->nonce_size = DEFAULT_NONCE_SIZE;
    494 	new->passive = FALSE;
    495 	new->ike_frag = FALSE;
    496 	new->esp_frag = IP_MAXPACKET;
    497 	new->ini_contact = TRUE;
    498 	new->mode_cfg = FALSE;
    499 	new->pcheck_level = PROP_CHECK_STRICT;
    500 	new->verify_identifier = FALSE;
    501 	new->verify_cert = TRUE;
    502 	new->cacertfile = NULL;
    503 	new->send_cert = TRUE;
    504 	new->send_cr = TRUE;
    505 	new->match_empty_cr = FALSE;
    506 	new->support_proxy = FALSE;
    507 	for (i = 0; i <= SCRIPT_MAX; i++)
    508 		new->script[i] = NULL;
    509 	new->gen_policy = FALSE;
    510 	new->nat_traversal = FALSE;
    511 	new->rsa_private = genlist_init();
    512 	new->rsa_public = genlist_init();
    513 	new->idv = NULL;
    514 	new->key = NULL;
    515 
    516 	new->dpd = TRUE; /* Enable DPD support by default */
    517 	new->dpd_interval = 0; /* Disable DPD checks by default */
    518 	new->dpd_retry = 5;
    519 	new->dpd_maxfails = 5;
    520 
    521 	new->rekey = REKEY_ON;
    522 
    523 	new->weak_phase1_check = 0;
    524 
    525 #ifdef ENABLE_HYBRID
    526 	new->xauth = NULL;
    527 #endif
    528 
    529 	new->lifetime = oakley_get_defaultlifetime();
    530 
    531 	return new;
    532 }
    533 
    534 static void *
    535 dupidvl(void *entry, void *arg)
    536 {
    537 	struct idspec *id;
    538 	struct idspec *old = (struct idspec *) entry;
    539 	id = newidspec();
    540 	if (!id) return (void *) -1;
    541 
    542 	if (set_identifier(&id->id, old->idtype, old->id) != 0) {
    543 		racoon_free(id);
    544 		return (void *) -1;
    545 	}
    546 
    547 	id->idtype = old->idtype;
    548 
    549 	genlist_append(arg, id);
    550 	return NULL;
    551 }
    552 
    553 static void *
    554 duprsa(void *entry, void *arg)
    555 {
    556 	struct rsa_key *new;
    557 
    558 	new = rsa_key_dup((struct rsa_key *)entry);
    559 	if (new == NULL)
    560 		return (void *) -1;
    561 	genlist_append(arg, new);
    562 
    563 	/* keep genlist_foreach going */
    564 	return NULL;
    565 }
    566 
    567 /* Creates shallow copy of a remote config. Used for "inherit" keyword. */
    568 struct remoteconf *
    569 duprmconf_shallow(struct remoteconf *rmconf)
    570 {
    571 	struct remoteconf *new;
    572 
    573 	new = racoon_calloc(1, sizeof(*new));
    574 	if (new == NULL)
    575 		return NULL;
    576 
    577 	memcpy(new, rmconf, sizeof(*new));
    578 	new->name = NULL;
    579 	new->inherited_from = rmconf;
    580 
    581 	new->proposal = NULL; /* will be filled by set_isakmp_proposal() */
    582 
    583 	/* Better to set remote to NULL to avoid that the destination
    584 	 * rmconf uses the same allocated memory as the source rmconf.
    585 	 */
    586 	new->remote = NULL;
    587 
    588 	return new;
    589 }
    590 
    591 /* Copies pointer structures of an inherited remote config.
    592  * Used by "inherit" mechanism in a two step copy method, necessary to
    593  * prevent both double free() and memory leak during config reload.
    594  */
    595 int
    596 duprmconf_finish(struct remoteconf *new)
    597 {
    598 	struct remoteconf *rmconf;
    599 	int i;
    600 
    601 	if (new->inherited_from == NULL)
    602 		return 0; /* nothing todo, no inheritance */
    603 
    604 	rmconf = new->inherited_from;
    605 
    606 	/* duplicate dynamic structures unless value overridden */
    607 	if (new->etypes != NULL && new->etypes == rmconf->etypes)
    608 		new->etypes = dupetypes(new->etypes);
    609 	if (new->idvl_p == rmconf->idvl_p) {
    610 		new->idvl_p = genlist_init();
    611 		genlist_foreach(rmconf->idvl_p, dupidvl, new->idvl_p);
    612 	}
    613 
    614 	if (new->rsa_private == rmconf->rsa_private) {
    615 		new->rsa_private = genlist_init();
    616 		genlist_foreach(rmconf->rsa_private, duprsa, new->rsa_private);
    617 	}
    618 	if (new->rsa_public == rmconf->rsa_public) {
    619 		new->rsa_public = genlist_init();
    620 		genlist_foreach(rmconf->rsa_public, duprsa, new->rsa_public);
    621 	}
    622 	if (new->remote != NULL && new->remote == rmconf->remote) {
    623 		new->remote = racoon_malloc(sizeof(*new->remote));
    624 		if (new->remote == NULL) {
    625 			plog(LLV_ERROR, LOCATION, NULL,
    626 			    "duprmconf_finish: malloc failed (remote)\n");
    627 			exit(1);
    628 		}
    629 		memcpy(new->remote, rmconf->remote, sizeof(*new->remote));
    630 	}
    631 	if (new->spspec != NULL && new->spspec == rmconf->spspec) {
    632 		dupspspec_list(new, rmconf);
    633 	}
    634 
    635 	/* proposal has been deep copied already from spspec's, see
    636 	 * cfparse.y:set_isakmp_proposal, which in turn calls
    637 	 * cfparse.y:expand_isakmpspec where the copying happens.
    638 	 */
    639 
    640 #ifdef ENABLE_HYBRID
    641 	if (new->xauth != NULL && new->xauth == rmconf->xauth) {
    642 		new->xauth = xauth_rmconf_dup(new->xauth);
    643 		if (new->xauth == NULL)
    644 			exit(1);
    645 	}
    646 #endif
    647 
    648         /* duplicate strings unless value overridden */
    649 	if (new->mycertfile != NULL && new->mycertfile == rmconf->mycertfile) {
    650 		new->mycertfile = racoon_strdup(new->mycertfile);
    651 		STRDUP_FATAL(new->mycertfile);
    652 	}
    653 	if (new->myprivfile != NULL && new->myprivfile == rmconf->myprivfile) {
    654 		new->myprivfile = racoon_strdup(new->myprivfile);
    655 		STRDUP_FATAL(new->myprivfile);
    656 	}
    657 	if (new->peerscertfile != NULL && new->peerscertfile == rmconf->peerscertfile) {
    658 		new->peerscertfile = racoon_strdup(new->peerscertfile);
    659 		STRDUP_FATAL(new->peerscertfile);
    660 	}
    661 	if (new->cacertfile != NULL && new->cacertfile == rmconf->cacertfile) {
    662 		new->cacertfile = racoon_strdup(new->cacertfile);
    663 		STRDUP_FATAL(new->cacertfile);
    664 	}
    665 	if (new->idv != NULL && new->idv == rmconf->idv) {
    666 		new->idv = vdup(new->idv);
    667 		STRDUP_FATAL(new->idv);
    668 	}
    669 	if (new->key != NULL && new->key == rmconf->key) {
    670 		new->key = vdup(new->key);
    671 		STRDUP_FATAL(new->key);
    672 	}
    673 	if (new->mycert != NULL && new->mycert == rmconf->mycert) {
    674 		new->mycert = vdup(new->mycert);
    675 		STRDUP_FATAL(new->mycert);
    676 	}
    677 	if (new->peerscert != NULL && new->peerscert == rmconf->peerscert) {
    678 		new->peerscert = vdup(new->peerscert);
    679 		STRDUP_FATAL(new->peerscert);
    680 	}
    681 	if (new->cacert != NULL && new->cacert == rmconf->cacert) {
    682 		new->cacert = vdup(new->cacert);
    683 		STRDUP_FATAL(new->cacert);
    684 	}
    685 	for (i = 0; i <= SCRIPT_MAX; i++)
    686 		if (new->script[i] != NULL && new->script[i] == rmconf->script[i]) {
    687 			new->script[i] = vdup(new->script[i]);
    688 			STRDUP_FATAL(new->script[i]);
    689 		}
    690 
    691 	return 0;
    692 }
    693 
    694 static void
    695 idspec_free(void *data)
    696 {
    697 	vfree (((struct idspec *)data)->id);
    698 	free (data);
    699 }
    700 
    701 void
    702 delrmconf(struct remoteconf *rmconf)
    703 {
    704 	int i;
    705 
    706 	if (rmconf == NULL)
    707 		return;
    708 
    709 #ifdef ENABLE_HYBRID
    710 	if (rmconf->xauth)
    711 		xauth_rmconf_delete(&rmconf->xauth);
    712 #endif
    713 	if (rmconf->etypes){
    714 		deletypes(rmconf->etypes);
    715 		rmconf->etypes=NULL;
    716 	}
    717 	if (rmconf->idv)
    718 		vfree(rmconf->idv);
    719 	if (rmconf->key)
    720 		vfree(rmconf->key);
    721 	if (rmconf->idvl_p)
    722 		genlist_free(rmconf->idvl_p, idspec_free);
    723 	if (rmconf->dhgrp)
    724 		oakley_dhgrp_free(rmconf->dhgrp);
    725 	if (rmconf->proposal)
    726 		delisakmpsa(rmconf->proposal);
    727 	flushspspec(rmconf);
    728 	if (rmconf->mycert)
    729 		vfree(rmconf->mycert);
    730 	if (rmconf->mycertfile)
    731 		racoon_free(rmconf->mycertfile);
    732 	if (rmconf->myprivfile)
    733 		racoon_free(rmconf->myprivfile);
    734 	if (rmconf->peerscert)
    735 		vfree(rmconf->peerscert);
    736 	if (rmconf->peerscertfile)
    737 		racoon_free(rmconf->peerscertfile);
    738 	if (rmconf->cacert)
    739 		vfree(rmconf->cacert);
    740 	if (rmconf->cacertfile)
    741 		racoon_free(rmconf->cacertfile);
    742 	if (rmconf->rsa_private)
    743 		genlist_free(rmconf->rsa_private, rsa_key_free);
    744 	if (rmconf->rsa_public)
    745 		genlist_free(rmconf->rsa_public, rsa_key_free);
    746 	if (rmconf->name)
    747 		racoon_free(rmconf->name);
    748 	if (rmconf->remote)
    749 		racoon_free(rmconf->remote);
    750 	for (i = 0; i <= SCRIPT_MAX; i++)
    751 		if (rmconf->script[i])
    752 			vfree(rmconf->script[i]);
    753 
    754 	racoon_free(rmconf);
    755 }
    756 
    757 void
    758 delisakmpsa(struct isakmpsa *sa)
    759 {
    760 	if (sa->dhgrp)
    761 		oakley_dhgrp_free(sa->dhgrp);
    762 	if (sa->next)
    763 		delisakmpsa(sa->next);
    764 #ifdef HAVE_GSSAPI
    765 	if (sa->gssid)
    766 		vfree(sa->gssid);
    767 #endif
    768 	racoon_free(sa);
    769 }
    770 
    771 struct etypes *
    772 dupetypes(struct etypes *orig)
    773 {
    774 	struct etypes *new;
    775 
    776 	if (!orig)
    777 		return NULL;
    778 
    779 	new = racoon_malloc(sizeof(struct etypes));
    780 	if (new == NULL)
    781 		return NULL;
    782 
    783 	new->type = orig->type;
    784 	new->next = NULL;
    785 
    786 	if (orig->next)
    787 		new->next=dupetypes(orig->next);
    788 
    789 	return new;
    790 }
    791 
    792 void
    793 deletypes(struct etypes *e)
    794 {
    795 	if (e->next)
    796 		deletypes(e->next);
    797 	racoon_free(e);
    798 }
    799 
    800 /*
    801  * insert into head of list.
    802  */
    803 void
    804 insrmconf(struct remoteconf *new)
    805 {
    806 	if (new->name == NULL) {
    807 		new->name = racoon_strdup(saddr2str(new->remote));
    808 	}
    809 	if (new->remote == NULL) {
    810 		new->remote = newsaddr(sizeof(struct sockaddr));
    811 		new->remote->sa_family = AF_UNSPEC;
    812 	}
    813 
    814 	TAILQ_INSERT_HEAD(&rmtree, new, chain);
    815 }
    816 
    817 void
    818 remrmconf(struct remoteconf *rmconf)
    819 {
    820 	TAILQ_REMOVE(&rmtree, rmconf, chain);
    821 }
    822 
    823 void
    824 flushrmconf(void)
    825 {
    826 	struct remoteconf *p, *next;
    827 
    828 	for (p = TAILQ_FIRST(&rmtree); p; p = next) {
    829 		next = TAILQ_NEXT(p, chain);
    830 		remrmconf(p);
    831 		delrmconf(p);
    832 	}
    833 }
    834 
    835 void
    836 initrmconf(void)
    837 {
    838 	TAILQ_INIT(&rmtree);
    839 }
    840 
    841 void
    842 rmconf_start_reload(void)
    843 {
    844 	rmtree_save=rmtree;
    845 	initrmconf();
    846 }
    847 
    848 void
    849 rmconf_finish_reload(void)
    850 {
    851 	remoteconf_tailq_head_t rmtree_tmp;
    852 
    853 	rmtree_tmp=rmtree;
    854 	rmtree=rmtree_save;
    855 	flushrmconf();
    856 	initrmconf();
    857 	rmtree=rmtree_tmp;
    858 }
    859 
    860 
    861 
    862 /* check exchange type to be acceptable */
    863 int
    864 check_etypeok(struct remoteconf *rmconf, void *ctx)
    865 {
    866 	u_int8_t etype = (u_int8_t) (intptr_t) ctx;
    867 	struct etypes *e;
    868 
    869 	for (e = rmconf->etypes; e != NULL; e = e->next) {
    870 		if (e->type == etype)
    871 			return 1;
    872 		plog(LLV_DEBUG2, LOCATION, NULL,
    873 		     "Etype mismatch: got %d, expected %d.\n", e->type, etype);
    874 	}
    875 
    876 	return 0;
    877 }
    878 
    879 /*%%%*/
    880 struct isakmpsa *
    881 newisakmpsa(void)
    882 {
    883 	struct isakmpsa *new;
    884 
    885 	new = racoon_calloc(1, sizeof(*new));
    886 	if (new == NULL)
    887 		return NULL;
    888 
    889 	/*
    890 	 * Just for sanity, make sure this is initialized.  This is
    891 	 * filled in for real when the ISAKMP proposal is configured.
    892 	 */
    893 	new->vendorid = VENDORID_UNKNOWN;
    894 
    895 	new->next = NULL;
    896 #ifdef HAVE_GSSAPI
    897 	new->gssid = NULL;
    898 #endif
    899 
    900 	return new;
    901 }
    902 
    903 /*
    904  * insert into tail of list.
    905  */
    906 void
    907 insisakmpsa(struct isakmpsa *new, struct remoteconf *rmconf)
    908 {
    909 	struct isakmpsa *p;
    910 
    911 	if (rmconf->proposal == NULL) {
    912 		rmconf->proposal = new;
    913 		return;
    914 	}
    915 
    916 	for (p = rmconf->proposal; p->next != NULL; p = p->next)
    917 		;
    918 	p->next = new;
    919 }
    920 
    921 /*ARGSUSED*/
    922 static void *
    923 dump_peers_identifiers (void *entry, void *arg __unused)
    924 {
    925 	struct idspec *id = (struct idspec*) entry;
    926 	char buf[1024], *pbuf;
    927 	pbuf = buf;
    928 	pbuf += sprintf (pbuf, "\tpeers_identifier %s",
    929 			 s_idtype (id->idtype));
    930 	if (id->id)
    931 		pbuf += sprintf (pbuf, " \"%s\"", id->id->v);
    932 	plog(LLV_INFO, LOCATION, NULL, "%s;\n", buf);
    933 	return NULL;
    934 }
    935 
    936 /*ARGSUSED*/
    937 static int
    938 dump_rmconf_single (struct remoteconf *p, void *data __unused)
    939 {
    940 	struct etypes *etype = p->etypes;
    941 	struct isakmpsa *prop = p->proposal;
    942 	char buf[1024], *pbuf;
    943 
    944 	pbuf = buf;
    945 
    946 	pbuf += sprintf(pbuf, "remote \"%s\"", p->name);
    947 	if (p->inherited_from)
    948 		pbuf += sprintf(pbuf, " inherit \"%s\"",
    949 				p->inherited_from->name);
    950 	plog(LLV_INFO, LOCATION, NULL, "%s {\n", buf);
    951 	pbuf = buf;
    952 	pbuf += sprintf(pbuf, "\texchange_type ");
    953 	while (etype) {
    954 		pbuf += sprintf (pbuf, "%s%s", s_etype(etype->type),
    955 				 etype->next != NULL ? ", " : ";\n");
    956 		etype = etype->next;
    957 	}
    958 	plog(LLV_INFO, LOCATION, NULL, "%s", buf);
    959 	plog(LLV_INFO, LOCATION, NULL, "\tdoi %s;\n", s_doi(p->doitype));
    960 	pbuf = buf;
    961 	pbuf += sprintf(pbuf, "\tmy_identifier %s", s_idtype (p->idvtype));
    962 	if (p->idvtype == IDTYPE_ASN1DN) {
    963 		plog(LLV_INFO, LOCATION, NULL, "%s;\n", buf);
    964 		plog(LLV_INFO, LOCATION, NULL,
    965 		     "\tcertificate_type %s \"%s\" \"%s\";\n",
    966 		     oakley_get_certtype(p->mycert) == ISAKMP_CERT_X509SIGN
    967 		       ? "x509" : "*UNKNOWN*",
    968 		     p->mycertfile, p->myprivfile);
    969 
    970 		switch (oakley_get_certtype(p->peerscert)) {
    971 		case ISAKMP_CERT_NONE:
    972 			plog(LLV_INFO, LOCATION, NULL,
    973 			     "\t/* peers certificate from payload */\n");
    974 			break;
    975 		case ISAKMP_CERT_X509SIGN:
    976 			plog(LLV_INFO, LOCATION, NULL,
    977 			     "\tpeers_certfile \"%s\";\n", p->peerscertfile);
    978 			break;
    979 		case ISAKMP_CERT_DNS:
    980 			plog(LLV_INFO, LOCATION, NULL,
    981 			     "\tpeers_certfile dnssec;\n");
    982 			break;
    983 		default:
    984 			plog(LLV_INFO, LOCATION, NULL,
    985 			     "\tpeers_certfile *UNKNOWN* (%d)\n",
    986 			     oakley_get_certtype(p->peerscert));
    987 			break;
    988 		}
    989 	}
    990 	else {
    991 		if (p->idv)
    992 			pbuf += sprintf (pbuf, " \"%s\"", p->idv->v);
    993 		plog(LLV_INFO, LOCATION, NULL, "%s;\n", buf);
    994 		genlist_foreach(p->idvl_p, &dump_peers_identifiers, NULL);
    995 	}
    996 
    997 	plog(LLV_INFO, LOCATION, NULL, "\trekey %s;\n",
    998 		p->rekey == REKEY_FORCE ? "force" : s_switch (p->rekey));
    999 	plog(LLV_INFO, LOCATION, NULL, "\tsend_cert %s;\n",
   1000 		s_switch (p->send_cert));
   1001 	plog(LLV_INFO, LOCATION, NULL, "\tsend_cr %s;\n",
   1002 		s_switch (p->send_cr));
   1003 	plog(LLV_INFO, LOCATION, NULL, "\tmatch_empty_cr %s;\n",
   1004 		s_switch (p->match_empty_cr));
   1005 	plog(LLV_INFO, LOCATION, NULL, "\tverify_cert %s;\n",
   1006 		s_switch (p->verify_cert));
   1007 	plog(LLV_INFO, LOCATION, NULL, "\tverify_identifier %s;\n",
   1008 		s_switch (p->verify_identifier));
   1009 	plog(LLV_INFO, LOCATION, NULL, "\tnat_traversal %s;\n",
   1010 		p->nat_traversal == NATT_FORCE ?
   1011 			"force" : s_switch (p->nat_traversal));
   1012 	plog(LLV_INFO, LOCATION, NULL, "\tnonce_size %d;\n",
   1013 		p->nonce_size);
   1014 	plog(LLV_INFO, LOCATION, NULL, "\tpassive %s;\n",
   1015 		s_switch (p->passive));
   1016 	plog(LLV_INFO, LOCATION, NULL, "\tike_frag %s;\n",
   1017 		p->ike_frag == ISAKMP_FRAG_FORCE ?
   1018 			"force" : s_switch (p->ike_frag));
   1019 	plog(LLV_INFO, LOCATION, NULL, "\tesp_frag %d;\n", p->esp_frag);
   1020 	plog(LLV_INFO, LOCATION, NULL, "\tinitial_contact %s;\n",
   1021 		s_switch (p->ini_contact));
   1022 	plog(LLV_INFO, LOCATION, NULL, "\tgenerate_policy %s;\n",
   1023 		s_switch (p->gen_policy));
   1024 	plog(LLV_INFO, LOCATION, NULL, "\tsupport_proxy %s;\n",
   1025 		s_switch (p->support_proxy));
   1026 
   1027 	while (prop) {
   1028 		plog(LLV_INFO, LOCATION, NULL, "\n");
   1029 		plog(LLV_INFO, LOCATION, NULL,
   1030 			"\t/* prop_no=%d, trns_no=%d */\n",
   1031 			prop->prop_no, prop->trns_no);
   1032 		plog(LLV_INFO, LOCATION, NULL, "\tproposal {\n");
   1033 		plog(LLV_INFO, LOCATION, NULL, "\t\tlifetime time %lu sec;\n",
   1034 			(long)prop->lifetime);
   1035 		plog(LLV_INFO, LOCATION, NULL, "\t\tlifetime bytes %zd;\n",
   1036 			prop->lifebyte);
   1037 		plog(LLV_INFO, LOCATION, NULL, "\t\tdh_group %s;\n",
   1038 			alg_oakley_dhdef_name(prop->dh_group));
   1039 		plog(LLV_INFO, LOCATION, NULL, "\t\tencryption_algorithm %s;\n",
   1040 			alg_oakley_encdef_name(prop->enctype));
   1041 		plog(LLV_INFO, LOCATION, NULL, "\t\thash_algorithm %s;\n",
   1042 			alg_oakley_hashdef_name(prop->hashtype));
   1043 		plog(LLV_INFO, LOCATION, NULL, "\t\tauthentication_method %s;\n",
   1044 			alg_oakley_authdef_name(prop->authmethod));
   1045 		plog(LLV_INFO, LOCATION, NULL, "\t}\n");
   1046 		prop = prop->next;
   1047 	}
   1048 	plog(LLV_INFO, LOCATION, NULL, "}\n");
   1049 	plog(LLV_INFO, LOCATION, NULL, "\n");
   1050 
   1051 	return 0;
   1052 }
   1053 
   1054 void
   1055 dumprmconf(void)
   1056 {
   1057 	enumrmconf(NULL, dump_rmconf_single, NULL);
   1058 }
   1059 
   1060 struct idspec *
   1061 newidspec(void)
   1062 {
   1063 	struct idspec *new;
   1064 
   1065 	new = racoon_calloc(1, sizeof(*new));
   1066 	if (new == NULL)
   1067 		return NULL;
   1068 	new->idtype = IDTYPE_ADDRESS;
   1069 	new->id = NULL;
   1070 	return new;
   1071 }
   1072 
   1073 vchar_t *
   1074 script_path_add(vchar_t *path)
   1075 {
   1076 	char *script_dir;
   1077 	vchar_t *new_path;
   1078 	size_t len;
   1079 
   1080 	script_dir = lcconf->pathinfo[LC_PATHTYPE_SCRIPT];
   1081 
   1082 	/* Try to find the script in the script directory */
   1083 	if ((path->v[0] != '/') && (script_dir != NULL)) {
   1084 		len = strlen(script_dir) + sizeof("/") + path->l + 1;
   1085 
   1086 		if ((new_path = vmalloc(len)) == NULL) {
   1087 			plog(LLV_ERROR, LOCATION, NULL,
   1088 			    "Cannot allocate memory: %s\n", strerror(errno));
   1089 			return NULL;
   1090 		}
   1091 
   1092 		new_path->v[0] = '\0';
   1093 		(void)strlcat(new_path->v, script_dir, len);
   1094 		(void)strlcat(new_path->v, "/", len);
   1095 		(void)strlcat(new_path->v, path->v, len);
   1096 
   1097 		vfree(path);
   1098 		path = new_path;
   1099 	}
   1100 
   1101 	return path;
   1102 }
   1103 
   1104 
   1105 struct isakmpsa *
   1106 dupisakmpsa(struct isakmpsa *sa)
   1107 {
   1108 	struct isakmpsa *res = NULL;
   1109 
   1110 	if(sa == NULL)
   1111 		return NULL;
   1112 
   1113 	res = newisakmpsa();
   1114 	if(res == NULL)
   1115 		return NULL;
   1116 
   1117 	*res = *sa;
   1118 #ifdef HAVE_GSSAPI
   1119 	if (sa->gssid != NULL)
   1120 		res->gssid = vdup(sa->gssid);
   1121 #endif
   1122 	res->next = NULL;
   1123 
   1124 	if(sa->dhgrp != NULL)
   1125 		oakley_setdhgroup(sa->dh_group, &res->dhgrp);
   1126 
   1127 	return res;
   1128 
   1129 }
   1130 
   1131 #ifdef ENABLE_HYBRID
   1132 int
   1133 isakmpsa_switch_authmethod(int authmethod)
   1134 {
   1135 	switch(authmethod) {
   1136 	case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R:
   1137 		authmethod = OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I;
   1138 		break;
   1139 	case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R:
   1140 		authmethod = OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I;
   1141 		break;
   1142 	case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R:
   1143 		authmethod = OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I;
   1144 		break;
   1145 	case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R:
   1146 		authmethod = OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I;
   1147 		break;
   1148 	case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R:
   1149 		authmethod = OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I;
   1150 		break;
   1151 	case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_R:
   1152 		authmethod = OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_I;
   1153 		break;
   1154 	case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_R:
   1155 		authmethod = OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_I;
   1156 		break;
   1157 	default:
   1158 		break;
   1159 	}
   1160 
   1161 	return authmethod;
   1162 }
   1163 #endif
   1164 
   1165 /*
   1166  * Given a proposed ISAKMP SA, and a list of acceptable
   1167  * ISAKMP SAs, it compares using pcheck_level policy and
   1168  * returns first match (if any).
   1169  */
   1170 struct isakmpsa *
   1171 checkisakmpsa(int pcheck_level, struct isakmpsa *proposal,
   1172     struct isakmpsa *acceptable)
   1173 {
   1174 	struct isakmpsa *p;
   1175 
   1176 	for (p = acceptable; p != NULL; p = p->next){
   1177 		plog(LLV_DEBUG2, LOCATION, NULL,
   1178 		     "checkisakmpsa:\nauthmethod: %d / %d\n",
   1179 		     isakmpsa_switch_authmethod(proposal->authmethod), isakmpsa_switch_authmethod(p->authmethod));
   1180 		if (isakmpsa_switch_authmethod(proposal->authmethod) != isakmpsa_switch_authmethod(p->authmethod) ||
   1181 		    proposal->enctype != p->enctype ||
   1182                     proposal->dh_group != p->dh_group ||
   1183 		    proposal->hashtype != p->hashtype)
   1184 			continue;
   1185 
   1186 		switch (pcheck_level) {
   1187 		case PROP_CHECK_OBEY:
   1188 			break;
   1189 
   1190 		case PROP_CHECK_CLAIM:
   1191 		case PROP_CHECK_STRICT:
   1192 			if (proposal->encklen < p->encklen ||
   1193 #if 0
   1194 			    proposal->lifebyte > p->lifebyte ||
   1195 #endif
   1196 			    proposal->lifetime > p->lifetime)
   1197 				continue;
   1198 			break;
   1199 
   1200 		case PROP_CHECK_EXACT:
   1201 			if (proposal->encklen != p->encklen ||
   1202 #if 0
   1203                             proposal->lifebyte != p->lifebyte ||
   1204 #endif
   1205 			    proposal->lifetime != p->lifetime)
   1206 				continue;
   1207 			break;
   1208 
   1209 		default:
   1210 			continue;
   1211 		}
   1212 
   1213 		return p;
   1214 	}
   1215 
   1216 	return NULL;
   1217 }
   1218