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