/* $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; }