| /* $NetBSD: remoteconf.c,v 1.26 2011/03/14 15:50:36 vanhu Exp $ */ |
| |
| /* Id: remoteconf.c,v 1.38 2006/05/06 15:52:44 manubsd Exp */ |
| |
| /* |
| * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the project nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <sys/socket.h> |
| #include <sys/queue.h> |
| |
| #include <netinet/in.h> |
| #include <netinet/in_systm.h> |
| #include <netinet/ip.h> |
| |
| #include PATH_IPSEC_H |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| |
| #include "var.h" |
| #include "misc.h" |
| #include "vmbuf.h" |
| #include "plog.h" |
| #include "sockmisc.h" |
| #include "genlist.h" |
| #include "debug.h" |
| |
| #include "isakmp_var.h" |
| #ifdef ENABLE_HYBRID |
| #include "isakmp_xauth.h" |
| #endif |
| #include "isakmp.h" |
| #include "ipsec_doi.h" |
| #include "crypto_openssl.h" |
| #include "oakley.h" |
| #include "remoteconf.h" |
| #include "localconf.h" |
| #include "grabmyaddr.h" |
| #include "policy.h" |
| #include "proposal.h" |
| #include "vendorid.h" |
| #include "gcmalloc.h" |
| #include "strnames.h" |
| #include "algorithm.h" |
| #include "nattraversal.h" |
| #include "isakmp_frag.h" |
| #include "handler.h" |
| #include "genlist.h" |
| #include "rsalist.h" |
| |
| typedef TAILQ_HEAD(_rmtree, remoteconf) remoteconf_tailq_head_t; |
| static remoteconf_tailq_head_t rmtree, rmtree_save; |
| |
| /* |
| * Script hook names and script hook paths |
| */ |
| char *script_names[SCRIPT_MAX + 1] = { |
| "phase1_up", "phase1_down", "phase1_dead" }; |
| |
| /*%%%*/ |
| |
| int |
| rmconf_match_identity(rmconf, id_p) |
| struct remoteconf *rmconf; |
| vchar_t *id_p; |
| { |
| struct ipsecdoi_id_b *id_b = (struct ipsecdoi_id_b *) id_p->v; |
| struct sockaddr *sa; |
| caddr_t sa1, sa2; |
| vchar_t ident; |
| struct idspec *id; |
| struct genlist_entry *gpb; |
| |
| /* compare with the ID if specified. */ |
| if (!genlist_next(rmconf->idvl_p, 0)) |
| return 0; |
| |
| for (id = genlist_next(rmconf->idvl_p, &gpb); id; id = genlist_next(0, &gpb)) { |
| /* No ID specified in configuration, so it is ok */ |
| if (id->id == 0) |
| return 0; |
| |
| /* check the type of both IDs */ |
| if (id->idtype != doi2idtype(id_b->type)) |
| continue; /* ID type mismatch */ |
| |
| /* compare defined ID with the ID sent by peer. */ |
| switch (id->idtype) { |
| case IDTYPE_ASN1DN: |
| ident.v = id_p->v + sizeof(*id_b); |
| ident.l = id_p->l - sizeof(*id_b); |
| if (eay_cmp_asn1dn(id->id, &ident) == 0) |
| return 0; |
| break; |
| case IDTYPE_ADDRESS: |
| sa = (struct sockaddr *)id->id->v; |
| sa2 = (caddr_t)(id_b + 1); |
| switch (sa->sa_family) { |
| case AF_INET: |
| if (id_p->l - sizeof(*id_b) != sizeof(struct in_addr)) |
| continue; /* ID value mismatch */ |
| sa1 = (caddr_t) &((struct sockaddr_in *)sa)->sin_addr; |
| if (memcmp(sa1, sa2, sizeof(struct in_addr)) == 0) |
| return 0; |
| break; |
| #ifdef INET6 |
| case AF_INET6: |
| if (id_p->l - sizeof(*id_b) != sizeof(struct in6_addr)) |
| continue; /* ID value mismatch */ |
| sa1 = (caddr_t) &((struct sockaddr_in6 *)sa)->sin6_addr; |
| if (memcmp(sa1, sa2, sizeof(struct in6_addr)) == 0) |
| return 0; |
| break; |
| #endif |
| default: |
| break; |
| } |
| break; |
| default: |
| if (memcmp(id->id->v, id_b + 1, id->id->l) == 0) |
| return 0; |
| break; |
| } |
| } |
| |
| plog(LLV_WARNING, LOCATION, NULL, "No ID match.\n"); |
| if (rmconf->verify_identifier) |
| return ISAKMP_NTYPE_INVALID_ID_INFORMATION; |
| |
| return 0; |
| } |
| |
| static int |
| rmconf_match_etype_and_approval(rmconf, etype, approval) |
| struct remoteconf *rmconf; |
| int etype; |
| struct isakmpsa *approval; |
| { |
| struct isakmpsa *p; |
| |
| if (check_etypeok(rmconf, (void *) (intptr_t) etype) == 0) |
| return ISAKMP_NTYPE_NO_PROPOSAL_CHOSEN; |
| |
| if (approval == NULL) |
| return 0; |
| |
| if (etype == ISAKMP_ETYPE_AGG && |
| approval->dh_group != rmconf->dh_group) |
| return ISAKMP_NTYPE_NO_PROPOSAL_CHOSEN; |
| |
| if (checkisakmpsa(rmconf->pcheck_level, approval, |
| rmconf->proposal) == NULL) |
| return ISAKMP_NTYPE_NO_PROPOSAL_CHOSEN; |
| |
| return 0; |
| } |
| |
| enum rmconf_match_t { |
| MATCH_NONE = 0, |
| MATCH_BASIC = 0x0000001, |
| MATCH_ADDRESS = 0x0000002, |
| MATCH_SA = 0x0000004, |
| MATCH_IDENTITY = 0x0000008, |
| MATCH_AUTH_IDENTITY = 0x0000010, |
| }; |
| |
| static int |
| rmconf_match_type(rmsel, rmconf) |
| struct rmconfselector *rmsel; |
| struct remoteconf *rmconf; |
| { |
| int ret = MATCH_NONE, tmp; |
| |
| /* No match at all: unwanted anonymous */ |
| if ((rmsel->flags & GETRMCONF_F_NO_ANONYMOUS) && |
| rmconf->remote->sa_family == AF_UNSPEC){ |
| plog(LLV_DEBUG2, LOCATION, rmsel->remote, |
| "Not matched: Anonymous conf.\n"); |
| return MATCH_NONE; |
| } |
| |
| if ((rmsel->flags & GETRMCONF_F_NO_PASSIVE) && rmconf->passive){ |
| plog(LLV_DEBUG2, LOCATION, rmsel->remote, |
| "Not matched: passive conf.\n"); |
| return MATCH_NONE; |
| } |
| |
| ret |= MATCH_BASIC; |
| |
| /* Check address */ |
| if (rmsel->remote != NULL) { |
| if (rmconf->remote->sa_family != AF_UNSPEC) { |
| if (cmpsaddr(rmsel->remote, rmconf->remote) == CMPSADDR_MISMATCH){ |
| plog(LLV_DEBUG2, LOCATION, rmsel->remote, |
| "Not matched: address mismatch.\n"); |
| return MATCH_NONE; |
| } |
| |
| /* Address matched */ |
| ret |= MATCH_ADDRESS; |
| } |
| } |
| |
| /* Check etype and approval */ |
| if (rmsel->etype != ISAKMP_ETYPE_NONE) { |
| tmp=rmconf_match_etype_and_approval(rmconf, rmsel->etype, |
| rmsel->approval); |
| if (tmp != 0){ |
| plog(LLV_DEBUG2, LOCATION, rmsel->remote, |
| "Not matched: etype (%d)/approval mismatch (%d).\n", rmsel->etype, tmp); |
| return MATCH_NONE; |
| } |
| ret |= MATCH_SA; |
| } |
| |
| /* Check identity */ |
| if (rmsel->identity != NULL && rmconf->verify_identifier) { |
| if (rmconf_match_identity(rmconf, rmsel->identity) != 0){ |
| plog(LLV_DEBUG2, LOCATION, rmsel->remote, |
| "Not matched: identity mismatch.\n"); |
| return MATCH_NONE; |
| } |
| ret |= MATCH_IDENTITY; |
| } |
| |
| /* Check certificate request */ |
| if (rmsel->certificate_request != NULL) { |
| if (oakley_get_certtype(rmsel->certificate_request) != |
| oakley_get_certtype(rmconf->mycert)){ |
| plog(LLV_DEBUG2, LOCATION, rmsel->remote, |
| "Not matched: cert type mismatch.\n"); |
| return MATCH_NONE; |
| } |
| |
| if (rmsel->certificate_request->l > 1) { |
| vchar_t *issuer; |
| |
| issuer = eay_get_x509asn1issuername(rmconf->mycert); |
| if (rmsel->certificate_request->l - 1 != issuer->l || |
| memcmp(rmsel->certificate_request->v + 1, |
| issuer->v, issuer->l) != 0) { |
| vfree(issuer); |
| plog(LLV_DEBUG2, LOCATION, rmsel->remote, |
| "Not matched: cert issuer mismatch.\n"); |
| return MATCH_NONE; |
| } |
| vfree(issuer); |
| } else { |
| if (!rmconf->match_empty_cr){ |
| plog(LLV_DEBUG2, LOCATION, rmsel->remote, |
| "Not matched: empty certificate request.\n"); |
| return MATCH_NONE; |
| } |
| } |
| |
| ret |= MATCH_AUTH_IDENTITY; |
| } |
| |
| return ret; |
| } |
| |
| void rmconf_selector_from_ph1(rmsel, iph1) |
| struct rmconfselector *rmsel; |
| struct ph1handle *iph1; |
| { |
| memset(rmsel, 0, sizeof(*rmsel)); |
| rmsel->flags = 0; |
| rmsel->remote = iph1->remote; |
| rmsel->etype = iph1->etype; |
| rmsel->approval = iph1->approval; |
| rmsel->identity = iph1->id_p; |
| rmsel->certificate_request = iph1->cr_p; |
| } |
| |
| int |
| enumrmconf(rmsel, enum_func, enum_arg) |
| struct rmconfselector *rmsel; |
| int (* enum_func)(struct remoteconf *rmconf, void *arg); |
| void *enum_arg; |
| { |
| struct remoteconf *p; |
| int ret = 0; |
| |
| RACOON_TAILQ_FOREACH_REVERSE(p, &rmtree, _rmtree, chain) { |
| plog(LLV_DEBUG2, LOCATION, rmsel->remote, |
| "Checking remote conf \"%s\" %s.\n", p->name, |
| p->remote->sa_family == AF_UNSPEC ? |
| "anonymous" : saddr2str(p->remote)); |
| |
| if (rmsel != NULL) { |
| if (rmconf_match_type(rmsel, p) == MATCH_NONE){ |
| plog(LLV_DEBUG2, LOCATION, rmsel->remote, |
| "Not matched.\n"); |
| continue; |
| } |
| } |
| |
| plog(LLV_DEBUG2, LOCATION, NULL, |
| "enumrmconf: \"%s\" matches.\n", p->name); |
| |
| ret = (*enum_func)(p, enum_arg); |
| if (ret) |
| break; |
| } |
| |
| return ret; |
| } |
| |
| struct rmconf_find_context { |
| struct rmconfselector sel; |
| |
| struct remoteconf *rmconf; |
| int match_type; |
| int num_found; |
| }; |
| |
| static int |
| rmconf_find(rmconf, ctx) |
| struct remoteconf *rmconf; |
| void *ctx; |
| { |
| struct rmconf_find_context *fctx = (struct rmconf_find_context *) ctx; |
| int match_type; |
| |
| /* First matching remote conf? */ |
| match_type = rmconf_match_type(&fctx->sel, rmconf); |
| |
| if (fctx->rmconf != NULL) { |
| /* More ambiguous matches are ignored. */ |
| if (match_type < fctx->match_type) |
| return 0; |
| |
| if (match_type == fctx->match_type) { |
| /* Ambiguous match */ |
| fctx->num_found++; |
| return 0; |
| } |
| } |
| |
| /* More exact match found */ |
| fctx->match_type = match_type; |
| fctx->num_found = 1; |
| fctx->rmconf = rmconf; |
| |
| return 0; |
| } |
| |
| /* |
| * search remote configuration. |
| * don't use port number to search if its value is either IPSEC_PORT_ANY. |
| * If matching anonymous entry, then new entry is copied from anonymous entry. |
| * If no anonymous entry found, then return NULL. |
| * OUT: NULL: NG |
| * Other: remote configuration entry. |
| */ |
| |
| struct remoteconf * |
| getrmconf(remote, flags) |
| struct sockaddr *remote; |
| int flags; |
| { |
| struct rmconf_find_context ctx; |
| int n = 0; |
| |
| memset(&ctx, 0, sizeof(ctx)); |
| ctx.sel.flags = flags; |
| ctx.sel.remote = remote; |
| |
| if (enumrmconf(&ctx.sel, rmconf_find, &ctx) != 0) { |
| plog(LLV_ERROR, LOCATION, remote, |
| "multiple exact configurations.\n"); |
| return NULL; |
| } |
| |
| if (ctx.rmconf == NULL) { |
| plog(LLV_DEBUG, LOCATION, remote, |
| "no remote configuration found.\n"); |
| return NULL; |
| } |
| |
| if (ctx.num_found != 1) { |
| plog(LLV_DEBUG, LOCATION, remote, |
| "multiple non-exact configurations found.\n"); |
| return NULL; |
| } |
| |
| plog(LLV_DEBUG, LOCATION, remote, |
| "configuration \"%s\" selected.\n", |
| ctx.rmconf->name); |
| |
| return ctx.rmconf; |
| } |
| |
| struct remoteconf * |
| getrmconf_by_ph1(iph1) |
| struct ph1handle *iph1; |
| { |
| struct rmconf_find_context ctx; |
| |
| memset(&ctx, 0, sizeof(ctx)); |
| rmconf_selector_from_ph1(&ctx.sel, iph1); |
| if (loglevel >= LLV_DEBUG) { |
| char *idstr = NULL; |
| |
| if (iph1->id_p != NULL) |
| idstr = ipsecdoi_id2str(iph1->id_p); |
| |
| plog(LLV_DEBUG, LOCATION, iph1->remote, |
| "getrmconf_by_ph1: remote %s, identity %s.\n", |
| saddr2str(iph1->remote), idstr ? idstr : "<any>"); |
| |
| if (idstr) |
| racoon_free(idstr); |
| } |
| |
| if (enumrmconf(&ctx.sel, rmconf_find, &ctx) != 0) { |
| plog(LLV_ERROR, LOCATION, iph1->remote, |
| "multiple exact configurations.\n"); |
| return RMCONF_ERR_MULTIPLE; |
| } |
| |
| if (ctx.rmconf == NULL) { |
| plog(LLV_DEBUG, LOCATION, iph1->remote, |
| "no remote configuration found\n"); |
| return NULL; |
| } |
| |
| if (ctx.num_found != 1) { |
| plog(LLV_DEBUG, LOCATION, iph1->remote, |
| "multiple non-exact configurations found.\n"); |
| return RMCONF_ERR_MULTIPLE; |
| } |
| |
| plog(LLV_DEBUG, LOCATION, iph1->remote, |
| "configuration \"%s\" selected.\n", |
| ctx.rmconf->name); |
| |
| return ctx.rmconf; |
| } |
| |
| struct remoteconf * |
| getrmconf_by_name(name) |
| const char *name; |
| { |
| struct remoteconf *p; |
| |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "getrmconf_by_name: remote \"%s\".\n", |
| name); |
| |
| RACOON_TAILQ_FOREACH_REVERSE(p, &rmtree, _rmtree, chain) { |
| if (p->name == NULL) |
| continue; |
| |
| if (strcmp(name, p->name) == 0) |
| return p; |
| } |
| |
| return NULL; |
| } |
| |
| struct remoteconf * |
| newrmconf() |
| { |
| struct remoteconf *new; |
| int i; |
| |
| new = racoon_calloc(1, sizeof(*new)); |
| if (new == NULL) |
| return NULL; |
| |
| new->proposal = NULL; |
| |
| /* set default */ |
| new->doitype = IPSEC_DOI; |
| new->sittype = IPSECDOI_SIT_IDENTITY_ONLY; |
| new->idvtype = IDTYPE_UNDEFINED; |
| new->idvl_p = genlist_init(); |
| new->nonce_size = DEFAULT_NONCE_SIZE; |
| new->passive = FALSE; |
| new->ike_frag = FALSE; |
| new->esp_frag = IP_MAXPACKET; |
| new->ini_contact = TRUE; |
| new->mode_cfg = FALSE; |
| new->pcheck_level = PROP_CHECK_STRICT; |
| new->verify_identifier = FALSE; |
| new->verify_cert = TRUE; |
| new->cacertfile = NULL; |
| new->send_cert = TRUE; |
| new->send_cr = TRUE; |
| new->match_empty_cr = FALSE; |
| new->support_proxy = FALSE; |
| for (i = 0; i <= SCRIPT_MAX; i++) |
| new->script[i] = NULL; |
| new->gen_policy = FALSE; |
| new->nat_traversal = FALSE; |
| new->rsa_private = genlist_init(); |
| new->rsa_public = genlist_init(); |
| new->idv = NULL; |
| new->key = NULL; |
| |
| new->dpd = TRUE; /* Enable DPD support by default */ |
| new->dpd_interval = 0; /* Disable DPD checks by default */ |
| new->dpd_retry = 5; |
| new->dpd_maxfails = 5; |
| |
| new->rekey = REKEY_ON; |
| |
| new->weak_phase1_check = 0; |
| |
| #ifdef ENABLE_HYBRID |
| new->xauth = NULL; |
| #endif |
| |
| new->lifetime = oakley_get_defaultlifetime(); |
| |
| return new; |
| } |
| |
| #ifndef ANDROID_PATCHED |
| |
| void * |
| dupidvl(entry, arg) |
| void *entry; |
| void *arg; |
| { |
| struct idspec *id; |
| struct idspec *old = (struct idspec *) entry; |
| id = newidspec(); |
| if (!id) return (void *) -1; |
| |
| if (set_identifier(&id->id, old->idtype, old->id) != 0) { |
| racoon_free(id); |
| return (void *) -1; |
| } |
| |
| id->idtype = old->idtype; |
| |
| genlist_append(arg, id); |
| return NULL; |
| } |
| |
| void * |
| duprsa(entry, arg) |
| void *entry; |
| void *arg; |
| { |
| struct rsa_key *new; |
| |
| new = rsa_key_dup((struct rsa_key *)entry); |
| if (new == NULL) |
| return (void *) -1; |
| genlist_append(arg, new); |
| |
| /* keep genlist_foreach going */ |
| return NULL; |
| } |
| |
| /* Creates shallow copy of a remote config. Used for "inherit" keyword. */ |
| struct remoteconf * |
| duprmconf_shallow (rmconf) |
| struct remoteconf *rmconf; |
| { |
| struct remoteconf *new; |
| struct proposalspec *prspec; |
| |
| new = racoon_calloc(1, sizeof(*new)); |
| if (new == NULL) |
| return NULL; |
| |
| memcpy(new, rmconf, sizeof(*new)); |
| new->name = NULL; |
| new->inherited_from = rmconf; |
| |
| new->proposal = NULL; /* will be filled by set_isakmp_proposal() */ |
| |
| return new; |
| } |
| |
| /* Copies pointer structures of an inherited remote config. |
| * Used by "inherit" mechanism in a two step copy method, necessary to |
| * prevent both double free() and memory leak during config reload. |
| */ |
| int |
| duprmconf_finish (new) |
| struct remoteconf *new; |
| { |
| struct remoteconf *rmconf; |
| int i; |
| |
| if (new->inherited_from == NULL) |
| return 0; /* nothing todo, no inheritance */ |
| |
| rmconf = new->inherited_from; |
| |
| /* duplicate dynamic structures unless value overridden */ |
| if (new->etypes != NULL && new->etypes == rmconf->etypes) |
| new->etypes = dupetypes(new->etypes); |
| if (new->idvl_p == rmconf->idvl_p) { |
| new->idvl_p = genlist_init(); |
| genlist_foreach(rmconf->idvl_p, dupidvl, new->idvl_p); |
| } |
| |
| if (new->rsa_private == rmconf->rsa_private) { |
| new->rsa_private = genlist_init(); |
| genlist_foreach(rmconf->rsa_private, duprsa, new->rsa_private); |
| } |
| if (new->rsa_public == rmconf->rsa_public) { |
| new->rsa_public = genlist_init(); |
| genlist_foreach(rmconf->rsa_public, duprsa, new->rsa_public); |
| } |
| if (new->remote != NULL && new->remote == rmconf->remote) { |
| new->remote = racoon_malloc(sizeof(*new->remote)); |
| if (new->remote == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "duprmconf_finish: malloc failed (remote)\n"); |
| exit(1); |
| } |
| memcpy(new->remote, rmconf->remote, sizeof(*new->remote)); |
| } |
| if (new->spspec != NULL && new->spspec == rmconf->spspec) { |
| dupspspec_list(new, rmconf); |
| } |
| |
| /* proposal has been deep copied already from spspec's, see |
| * cfparse.y:set_isakmp_proposal, which in turn calls |
| * cfparse.y:expand_isakmpspec where the copying happens. |
| */ |
| |
| #ifdef ENABLE_HYBRID |
| if (new->xauth != NULL && new->xauth == rmconf->xauth) { |
| new->xauth = xauth_rmconf_dup(new->xauth); |
| if (new->xauth == NULL) |
| exit(1); |
| } |
| #endif |
| |
| /* duplicate strings unless value overridden */ |
| if (new->mycertfile != NULL && new->mycertfile == rmconf->mycertfile) { |
| new->mycertfile = racoon_strdup(new->mycertfile); |
| STRDUP_FATAL(new->mycertfile); |
| } |
| if (new->myprivfile != NULL && new->myprivfile == rmconf->myprivfile) { |
| new->myprivfile = racoon_strdup(new->myprivfile); |
| STRDUP_FATAL(new->myprivfile); |
| } |
| if (new->peerscertfile != NULL && new->peerscertfile == rmconf->peerscertfile) { |
| new->peerscertfile = racoon_strdup(new->peerscertfile); |
| STRDUP_FATAL(new->peerscertfile); |
| } |
| if (new->cacertfile != NULL && new->cacertfile == rmconf->cacertfile) { |
| new->cacertfile = racoon_strdup(new->cacertfile); |
| STRDUP_FATAL(new->cacertfile); |
| } |
| if (new->idv != NULL && new->idv == rmconf->idv) { |
| new->idv = vdup(new->idv); |
| STRDUP_FATAL(new->idv); |
| } |
| if (new->key != NULL && new->key == rmconf->key) { |
| new->key = vdup(new->key); |
| STRDUP_FATAL(new->key); |
| } |
| if (new->mycert != NULL && new->mycert == rmconf->mycert) { |
| new->mycert = vdup(new->mycert); |
| STRDUP_FATAL(new->mycert); |
| } |
| if (new->peerscert != NULL && new->peerscert == rmconf->peerscert) { |
| new->peerscert = vdup(new->peerscert); |
| STRDUP_FATAL(new->peerscert); |
| } |
| if (new->cacert != NULL && new->cacert == rmconf->cacert) { |
| new->cacert = vdup(new->cacert); |
| STRDUP_FATAL(new->cacert); |
| } |
| for (i = 0; i <= SCRIPT_MAX; i++) |
| if (new->script[i] != NULL && new->script[i] == rmconf->script[i]) { |
| new->script[i] = vdup(new->script[i]); |
| STRDUP_FATAL(new->script[i]); |
| } |
| |
| return 0; |
| } |
| |
| static void |
| idspec_free(void *data) |
| { |
| vfree (((struct idspec *)data)->id); |
| free (data); |
| } |
| |
| void |
| delrmconf(rmconf) |
| struct remoteconf *rmconf; |
| { |
| int i; |
| |
| #ifdef ENABLE_HYBRID |
| if (rmconf->xauth) |
| xauth_rmconf_delete(&rmconf->xauth); |
| #endif |
| if (rmconf->etypes){ |
| deletypes(rmconf->etypes); |
| rmconf->etypes=NULL; |
| } |
| if (rmconf->idv) |
| vfree(rmconf->idv); |
| if (rmconf->key) |
| vfree(rmconf->key); |
| if (rmconf->idvl_p) |
| genlist_free(rmconf->idvl_p, idspec_free); |
| if (rmconf->dhgrp) |
| oakley_dhgrp_free(rmconf->dhgrp); |
| if (rmconf->proposal) |
| delisakmpsa(rmconf->proposal); |
| flushspspec(rmconf); |
| if (rmconf->mycert) |
| vfree(rmconf->mycert); |
| if (rmconf->mycertfile) |
| racoon_free(rmconf->mycertfile); |
| if (rmconf->myprivfile) |
| racoon_free(rmconf->myprivfile); |
| if (rmconf->peerscert) |
| vfree(rmconf->peerscert); |
| if (rmconf->peerscertfile) |
| racoon_free(rmconf->peerscertfile); |
| if (rmconf->cacert) |
| vfree(rmconf->cacert); |
| if (rmconf->cacertfile) |
| racoon_free(rmconf->cacertfile); |
| if (rmconf->rsa_private) |
| genlist_free(rmconf->rsa_private, rsa_key_free); |
| if (rmconf->rsa_public) |
| genlist_free(rmconf->rsa_public, rsa_key_free); |
| if (rmconf->name) |
| racoon_free(rmconf->name); |
| if (rmconf->remote) |
| racoon_free(rmconf->remote); |
| for (i = 0; i <= SCRIPT_MAX; i++) |
| if (rmconf->script[i]) |
| vfree(rmconf->script[i]); |
| |
| racoon_free(rmconf); |
| } |
| |
| #else |
| |
| void delrmconf(struct remoteconf *rmconf) |
| { |
| exit(1); |
| } |
| |
| #endif |
| |
| void |
| delisakmpsa(sa) |
| struct isakmpsa *sa; |
| { |
| if (sa->dhgrp) |
| oakley_dhgrp_free(sa->dhgrp); |
| if (sa->next) |
| delisakmpsa(sa->next); |
| #ifdef HAVE_GSSAPI |
| if (sa->gssid) |
| vfree(sa->gssid); |
| #endif |
| racoon_free(sa); |
| } |
| |
| struct etypes * |
| dupetypes(orig) |
| struct etypes *orig; |
| { |
| struct etypes *new; |
| |
| if (!orig) |
| return NULL; |
| |
| new = racoon_malloc(sizeof(struct etypes)); |
| if (new == NULL) |
| return NULL; |
| |
| new->type = orig->type; |
| new->next = NULL; |
| |
| if (orig->next) |
| new->next=dupetypes(orig->next); |
| |
| return new; |
| } |
| |
| void |
| deletypes(e) |
| struct etypes *e; |
| { |
| if (e->next) |
| deletypes(e->next); |
| racoon_free(e); |
| } |
| |
| /* |
| * insert into head of list. |
| */ |
| void |
| insrmconf(new) |
| struct remoteconf *new; |
| { |
| if (new->name == NULL) { |
| new->name = racoon_strdup(saddr2str(new->remote)); |
| } |
| if (new->remote == NULL) { |
| new->remote = newsaddr(sizeof(struct sockaddr)); |
| new->remote->sa_family = AF_UNSPEC; |
| } |
| |
| TAILQ_INSERT_HEAD(&rmtree, new, chain); |
| } |
| |
| void |
| remrmconf(rmconf) |
| struct remoteconf *rmconf; |
| { |
| TAILQ_REMOVE(&rmtree, rmconf, chain); |
| } |
| |
| void |
| flushrmconf() |
| { |
| struct remoteconf *p, *next; |
| |
| for (p = TAILQ_FIRST(&rmtree); p; p = next) { |
| next = TAILQ_NEXT(p, chain); |
| remrmconf(p); |
| delrmconf(p); |
| } |
| } |
| |
| void |
| initrmconf() |
| { |
| TAILQ_INIT(&rmtree); |
| } |
| |
| void |
| rmconf_start_reload() |
| { |
| rmtree_save=rmtree; |
| initrmconf(); |
| } |
| |
| void |
| rmconf_finish_reload() |
| { |
| remoteconf_tailq_head_t rmtree_tmp; |
| |
| rmtree_tmp=rmtree; |
| rmtree=rmtree_save; |
| flushrmconf(); |
| initrmconf(); |
| rmtree=rmtree_tmp; |
| } |
| |
| |
| |
| /* check exchange type to be acceptable */ |
| int |
| check_etypeok(rmconf, ctx) |
| struct remoteconf *rmconf; |
| void *ctx; |
| { |
| u_int8_t etype = (u_int8_t) (intptr_t) ctx; |
| struct etypes *e; |
| |
| for (e = rmconf->etypes; e != NULL; e = e->next) { |
| if (e->type == etype) |
| return 1; |
| plog(LLV_DEBUG2, LOCATION, NULL, |
| "Etype mismatch: got %d, expected %d.\n", e->type, etype); |
| } |
| |
| return 0; |
| } |
| |
| /*%%%*/ |
| struct isakmpsa * |
| newisakmpsa() |
| { |
| struct isakmpsa *new; |
| |
| new = racoon_calloc(1, sizeof(*new)); |
| if (new == NULL) |
| return NULL; |
| |
| /* |
| * Just for sanity, make sure this is initialized. This is |
| * filled in for real when the ISAKMP proposal is configured. |
| */ |
| new->vendorid = VENDORID_UNKNOWN; |
| |
| new->next = NULL; |
| #ifdef HAVE_GSSAPI |
| new->gssid = NULL; |
| #endif |
| |
| return new; |
| } |
| |
| /* |
| * insert into tail of list. |
| */ |
| void |
| insisakmpsa(new, rmconf) |
| struct isakmpsa *new; |
| struct remoteconf *rmconf; |
| { |
| struct isakmpsa *p; |
| |
| if (rmconf->proposal == NULL) { |
| rmconf->proposal = new; |
| return; |
| } |
| |
| for (p = rmconf->proposal; p->next != NULL; p = p->next) |
| ; |
| p->next = new; |
| } |
| |
| static void * |
| dump_peers_identifiers (void *entry, void *arg) |
| { |
| struct idspec *id = (struct idspec*) entry; |
| char buf[1024], *pbuf; |
| pbuf = buf; |
| pbuf += sprintf (pbuf, "\tpeers_identifier %s", |
| s_idtype (id->idtype)); |
| if (id->id) |
| pbuf += sprintf (pbuf, " \"%s\"", id->id->v); |
| plog(LLV_INFO, LOCATION, NULL, "%s;\n", buf); |
| return NULL; |
| } |
| |
| static int |
| dump_rmconf_single (struct remoteconf *p, void *data) |
| { |
| struct etypes *etype = p->etypes; |
| struct isakmpsa *prop = p->proposal; |
| char buf[1024], *pbuf; |
| |
| pbuf = buf; |
| |
| pbuf += sprintf(pbuf, "remote \"%s\"", p->name); |
| if (p->inherited_from) |
| pbuf += sprintf(pbuf, " inherit \"%s\"", |
| p->inherited_from->name); |
| plog(LLV_INFO, LOCATION, NULL, "%s {\n", buf); |
| pbuf = buf; |
| pbuf += sprintf(pbuf, "\texchange_type "); |
| while (etype) { |
| pbuf += sprintf (pbuf, "%s%s", s_etype(etype->type), |
| etype->next != NULL ? ", " : ";\n"); |
| etype = etype->next; |
| } |
| plog(LLV_INFO, LOCATION, NULL, "%s", buf); |
| plog(LLV_INFO, LOCATION, NULL, "\tdoi %s;\n", s_doi(p->doitype)); |
| pbuf = buf; |
| pbuf += sprintf(pbuf, "\tmy_identifier %s", s_idtype (p->idvtype)); |
| if (p->idvtype == IDTYPE_ASN1DN) { |
| plog(LLV_INFO, LOCATION, NULL, "%s;\n", buf); |
| plog(LLV_INFO, LOCATION, NULL, |
| "\tcertificate_type %s \"%s\" \"%s\";\n", |
| oakley_get_certtype(p->mycert) == ISAKMP_CERT_X509SIGN |
| ? "x509" : "*UNKNOWN*", |
| p->mycertfile, p->myprivfile); |
| |
| switch (oakley_get_certtype(p->peerscert)) { |
| case ISAKMP_CERT_NONE: |
| plog(LLV_INFO, LOCATION, NULL, |
| "\t/* peers certificate from payload */\n"); |
| break; |
| case ISAKMP_CERT_X509SIGN: |
| plog(LLV_INFO, LOCATION, NULL, |
| "\tpeers_certfile \"%s\";\n", p->peerscertfile); |
| break; |
| case ISAKMP_CERT_DNS: |
| plog(LLV_INFO, LOCATION, NULL, |
| "\tpeers_certfile dnssec;\n"); |
| break; |
| default: |
| plog(LLV_INFO, LOCATION, NULL, |
| "\tpeers_certfile *UNKNOWN* (%d)\n", |
| oakley_get_certtype(p->peerscert)); |
| break; |
| } |
| } |
| else { |
| if (p->idv) |
| pbuf += sprintf (pbuf, " \"%s\"", p->idv->v); |
| plog(LLV_INFO, LOCATION, NULL, "%s;\n", buf); |
| genlist_foreach(p->idvl_p, &dump_peers_identifiers, NULL); |
| } |
| |
| plog(LLV_INFO, LOCATION, NULL, "\trekey %s;\n", |
| p->rekey == REKEY_FORCE ? "force" : s_switch (p->rekey)); |
| plog(LLV_INFO, LOCATION, NULL, "\tsend_cert %s;\n", |
| s_switch (p->send_cert)); |
| plog(LLV_INFO, LOCATION, NULL, "\tsend_cr %s;\n", |
| s_switch (p->send_cr)); |
| plog(LLV_INFO, LOCATION, NULL, "\tmatch_empty_cr %s;\n", |
| s_switch (p->match_empty_cr)); |
| plog(LLV_INFO, LOCATION, NULL, "\tverify_cert %s;\n", |
| s_switch (p->verify_cert)); |
| plog(LLV_INFO, LOCATION, NULL, "\tverify_identifier %s;\n", |
| s_switch (p->verify_identifier)); |
| plog(LLV_INFO, LOCATION, NULL, "\tnat_traversal %s;\n", |
| p->nat_traversal == NATT_FORCE ? |
| "force" : s_switch (p->nat_traversal)); |
| plog(LLV_INFO, LOCATION, NULL, "\tnonce_size %d;\n", |
| p->nonce_size); |
| plog(LLV_INFO, LOCATION, NULL, "\tpassive %s;\n", |
| s_switch (p->passive)); |
| plog(LLV_INFO, LOCATION, NULL, "\tike_frag %s;\n", |
| p->ike_frag == ISAKMP_FRAG_FORCE ? |
| "force" : s_switch (p->ike_frag)); |
| plog(LLV_INFO, LOCATION, NULL, "\tesp_frag %d;\n", p->esp_frag); |
| plog(LLV_INFO, LOCATION, NULL, "\tinitial_contact %s;\n", |
| s_switch (p->ini_contact)); |
| plog(LLV_INFO, LOCATION, NULL, "\tgenerate_policy %s;\n", |
| s_switch (p->gen_policy)); |
| plog(LLV_INFO, LOCATION, NULL, "\tsupport_proxy %s;\n", |
| s_switch (p->support_proxy)); |
| |
| while (prop) { |
| plog(LLV_INFO, LOCATION, NULL, "\n"); |
| plog(LLV_INFO, LOCATION, NULL, |
| "\t/* prop_no=%d, trns_no=%d */\n", |
| prop->prop_no, prop->trns_no); |
| plog(LLV_INFO, LOCATION, NULL, "\tproposal {\n"); |
| plog(LLV_INFO, LOCATION, NULL, "\t\tlifetime time %lu sec;\n", |
| (long)prop->lifetime); |
| plog(LLV_INFO, LOCATION, NULL, "\t\tlifetime bytes %zd;\n", |
| prop->lifebyte); |
| plog(LLV_INFO, LOCATION, NULL, "\t\tdh_group %s;\n", |
| alg_oakley_dhdef_name(prop->dh_group)); |
| plog(LLV_INFO, LOCATION, NULL, "\t\tencryption_algorithm %s;\n", |
| alg_oakley_encdef_name(prop->enctype)); |
| plog(LLV_INFO, LOCATION, NULL, "\t\thash_algorithm %s;\n", |
| alg_oakley_hashdef_name(prop->hashtype)); |
| plog(LLV_INFO, LOCATION, NULL, "\t\tauthentication_method %s;\n", |
| alg_oakley_authdef_name(prop->authmethod)); |
| plog(LLV_INFO, LOCATION, NULL, "\t}\n"); |
| prop = prop->next; |
| } |
| plog(LLV_INFO, LOCATION, NULL, "}\n"); |
| plog(LLV_INFO, LOCATION, NULL, "\n"); |
| |
| return 0; |
| } |
| |
| void |
| dumprmconf() |
| { |
| enumrmconf(NULL, dump_rmconf_single, NULL); |
| } |
| |
| struct idspec * |
| newidspec() |
| { |
| struct idspec *new; |
| |
| new = racoon_calloc(1, sizeof(*new)); |
| if (new == NULL) |
| return NULL; |
| new->idtype = IDTYPE_ADDRESS; |
| |
| return new; |
| } |
| |
| vchar_t * |
| script_path_add(path) |
| vchar_t *path; |
| { |
| char *script_dir; |
| vchar_t *new_path; |
| vchar_t *new_storage; |
| vchar_t **sp; |
| size_t len; |
| size_t size; |
| |
| script_dir = lcconf->pathinfo[LC_PATHTYPE_SCRIPT]; |
| |
| /* Try to find the script in the script directory */ |
| if ((path->v[0] != '/') && (script_dir != NULL)) { |
| len = strlen(script_dir) + sizeof("/") + path->l + 1; |
| |
| if ((new_path = vmalloc(len)) == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "Cannot allocate memory: %s\n", strerror(errno)); |
| return NULL; |
| } |
| |
| new_path->v[0] = '\0'; |
| (void)strlcat(new_path->v, script_dir, len); |
| (void)strlcat(new_path->v, "/", len); |
| (void)strlcat(new_path->v, path->v, len); |
| |
| vfree(path); |
| path = new_path; |
| } |
| |
| return path; |
| } |
| |
| |
| struct isakmpsa * |
| dupisakmpsa(struct isakmpsa *sa) |
| { |
| struct isakmpsa *res = NULL; |
| |
| if(sa == NULL) |
| return NULL; |
| |
| res = newisakmpsa(); |
| if(res == NULL) |
| return NULL; |
| |
| *res = *sa; |
| #ifdef HAVE_GSSAPI |
| if (sa->gssid != NULL) |
| res->gssid = vdup(sa->gssid); |
| #endif |
| res->next = NULL; |
| |
| if(sa->dhgrp != NULL) |
| oakley_setdhgroup(sa->dh_group, &res->dhgrp); |
| |
| return res; |
| |
| } |
| |
| #ifdef ENABLE_HYBRID |
| int |
| isakmpsa_switch_authmethod(authmethod) |
| int authmethod; |
| { |
| switch(authmethod) { |
| case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R: |
| authmethod = OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I; |
| break; |
| case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R: |
| authmethod = OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I; |
| break; |
| case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R: |
| authmethod = OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I; |
| break; |
| case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R: |
| authmethod = OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I; |
| break; |
| case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R: |
| authmethod = OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I; |
| break; |
| case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_R: |
| authmethod = OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_I; |
| break; |
| case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_R: |
| authmethod = OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_I; |
| break; |
| default: |
| break; |
| } |
| |
| return authmethod; |
| } |
| #endif |
| |
| /* |
| * Given a proposed ISAKMP SA, and a list of acceptable |
| * ISAKMP SAs, it compares using pcheck_level policy and |
| * returns first match (if any). |
| */ |
| struct isakmpsa * |
| checkisakmpsa(pcheck_level, proposal, acceptable) |
| int pcheck_level; |
| struct isakmpsa *proposal, *acceptable; |
| { |
| struct isakmpsa *p; |
| |
| for (p = acceptable; p != NULL; p = p->next){ |
| plog(LLV_DEBUG2, LOCATION, NULL, |
| "checkisakmpsa:\nauthmethod: %d / %d\n", |
| isakmpsa_switch_authmethod(proposal->authmethod), isakmpsa_switch_authmethod(p->authmethod)); |
| if (isakmpsa_switch_authmethod(proposal->authmethod) != isakmpsa_switch_authmethod(p->authmethod) || |
| proposal->enctype != p->enctype || |
| proposal->dh_group != p->dh_group || |
| proposal->hashtype != p->hashtype) |
| continue; |
| |
| switch (pcheck_level) { |
| case PROP_CHECK_OBEY: |
| break; |
| |
| case PROP_CHECK_CLAIM: |
| case PROP_CHECK_STRICT: |
| if (proposal->encklen < p->encklen || |
| #if 0 |
| proposal->lifebyte > p->lifebyte || |
| #endif |
| proposal->lifetime > p->lifetime) |
| continue; |
| break; |
| |
| case PROP_CHECK_EXACT: |
| if (proposal->encklen != p->encklen || |
| #if 0 |
| proposal->lifebyte != p->lifebyte || |
| #endif |
| proposal->lifetime != p->lifetime) |
| continue; |
| break; |
| |
| default: |
| continue; |
| } |
| |
| return p; |
| } |
| |
| return NULL; |
| } |