/* $NetBSD: remoteconf.c,v 1.9.4.2 2008/06/18 07:30:19 mgrooms 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 "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 "genlist.h" static TAILQ_HEAD(_rmtree, remoteconf) rmtree, rmtree_save, rmtree_tmp; /* * Script hook names and script hook paths */ char *script_names[SCRIPT_MAX + 1] = { "phase1_up", "phase1_down" }; /*%%%*/ /* * 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_strict(remote, allow_anon) struct sockaddr *remote; int allow_anon; { struct remoteconf *p; struct remoteconf *anon = NULL; int withport; char buf[NI_MAXHOST + NI_MAXSERV + 10]; char addr[NI_MAXHOST], port[NI_MAXSERV]; withport = 0; #ifndef ENABLE_NATT /* * We never have ports set in our remote configurations, but when * NAT-T is enabled, the kernel can have policies with ports and * send us an acquire message for a destination that has a port set. * If we do this port check here, we don't find the remote config. * * In an ideal world, we would be able to have remote conf with * port, and the port could be a wildcard. That test could be used. */ if (remote->sa_family != AF_UNSPEC && extract_port(remote) != IPSEC_PORT_ANY) withport = 1; #endif /* ENABLE_NATT */ if (remote->sa_family == AF_UNSPEC) snprintf (buf, sizeof(buf), "%s", "anonymous"); else { GETNAMEINFO(remote, addr, port); snprintf(buf, sizeof(buf), "%s%s%s%s", addr, withport ? "[" : "", withport ? port : "", withport ? "]" : ""); } TAILQ_FOREACH(p, &rmtree, chain) { if ((remote->sa_family == AF_UNSPEC && remote->sa_family == p->remote->sa_family) || (!withport && cmpsaddrwop(remote, p->remote) == 0) || (withport && cmpsaddrstrict(remote, p->remote) == 0)) { plog(LLV_DEBUG, LOCATION, NULL, "configuration found for %s.\n", buf); return p; } /* save the pointer to the anonymous configuration */ if (p->remote->sa_family == AF_UNSPEC) anon = p; } if (allow_anon && anon != NULL) { plog(LLV_DEBUG, LOCATION, NULL, "anonymous configuration selected for %s.\n", buf); return anon; } plog(LLV_DEBUG, LOCATION, NULL, "no remote configuration found.\n"); return NULL; } struct remoteconf * getrmconf(remote) struct sockaddr *remote; { return getrmconf_strict(remote, 1); } 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->getcert_method = ISAKMP_GETCERT_PAYLOAD; new->getcacert_method = ISAKMP_GETCERT_LOCALFILE; new->cacerttype = ISAKMP_CERT_X509SIGN; new->certtype = ISAKMP_CERT_NONE; new->cacertfile = NULL; new->send_cert = TRUE; new->send_cr = TRUE; new->support_proxy = FALSE; for (i = 0; i <= SCRIPT_MAX; i++) new->script[i] = NULL; new->gen_policy = FALSE; new->retry_counter = lcconf->retry_counter; new->retry_interval = lcconf->retry_interval; 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->weak_phase1_check = 0; #ifdef ENABLE_HYBRID new->xauth = NULL; #endif return new; } struct remoteconf * copyrmconf(remote) struct sockaddr *remote; { struct remoteconf *new, *old; old = getrmconf_strict (remote, 0); if (old == NULL) { plog (LLV_ERROR, LOCATION, NULL, "Remote configuration for '%s' not found!\n", saddr2str (remote)); return NULL; } new = duprmconf (old); return new; } 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; } struct remoteconf * duprmconf (rmconf) struct remoteconf *rmconf; { struct remoteconf *new; new = racoon_calloc(1, sizeof(*new)); if (new == NULL) return NULL; memcpy (new, rmconf, sizeof (*new)); // FIXME: We should duplicate the proposal as well. // This is now handled in the cfparse.y // new->proposal = ...; /* duplicate dynamic structures */ if (new->etypes) new->etypes=dupetypes(new->etypes); new->idvl_p = genlist_init(); genlist_foreach(rmconf->idvl_p, dupidvl, new->idvl_p); return new; } static void idspec_free(void *data) { vfree (((struct idspec *)data)->id); free (data); } void delrmconf(rmconf) struct remoteconf *rmconf; { #ifdef ENABLE_HYBRID if (rmconf->xauth) xauth_rmconf_delete(&rmconf->xauth); #endif if (rmconf->etypes){ deletypes(rmconf->etypes); rmconf->etypes=NULL; } 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); racoon_free(rmconf); } 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; { 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 save_rmconf() { rmtree_save=rmtree; initrmconf(); } void save_rmconf_flush() { rmtree_tmp=rmtree; rmtree=rmtree_save; flushrmconf(); initrmconf(); rmtree=rmtree_tmp; } /* check exchange type to be acceptable */ struct etypes * check_etypeok(rmconf, etype) struct remoteconf *rmconf; u_int8_t etype; { struct etypes *e; for (e = rmconf->etypes; e != NULL; e = e->next) { if (e->type == etype) break; } return e; } /*%%%*/ 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; new->rmconf = 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; new->rmconf = rmconf; if (rmconf->proposal == NULL) { rmconf->proposal = new; return; } for (p = rmconf->proposal; p->next != NULL; p = p->next) ; p->next = new; return; } struct remoteconf * foreachrmconf(rmconf_func_t rmconf_func, void *data) { struct remoteconf *p, *ret = NULL; RACOON_TAILQ_FOREACH_REVERSE(p, &rmtree, _rmtree, chain) { ret = (*rmconf_func)(p, data); if (ret) break; } return ret; } 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 struct remoteconf * 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", saddr2str(p->remote)); if (p->inherited_from) pbuf += sprintf(pbuf, " inherit %s", saddr2str(p->inherited_from->remote)); 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", p->certtype == ISAKMP_CERT_X509SIGN ? "x509" : "*UNKNOWN*", p->mycertfile, p->myprivfile); switch (p->getcert_method) { case 0: break; case ISAKMP_GETCERT_PAYLOAD: plog(LLV_INFO, LOCATION, NULL, "\t/* peers certificate from payload */\n"); break; case ISAKMP_GETCERT_LOCALFILE: plog(LLV_INFO, LOCATION, NULL, "\tpeers_certfile \"%s\";\n", p->peerscertfile); break; case ISAKMP_GETCERT_DNS: plog(LLV_INFO, LOCATION, NULL, "\tpeer_certfile dnssec;\n"); break; default: plog(LLV_INFO, LOCATION, NULL, "\tpeers_certfile *UNKNOWN* (%d)\n", p->getcert_method); } } 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, "\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, "\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, rmconf=%s */\n", prop->prop_no, prop->trns_no, saddr2str(prop->rmconf->remote)); 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 NULL; } void dumprmconf() { foreachrmconf (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 /* XXX gssid */ #endif res->next=NULL; if(sa->dhgrp != NULL) oakley_setdhgroup (sa->dh_group, &(res->dhgrp)); return res; }