/* $NetBSD: isakmp_cfg.c,v 1.12.6.4 2008/11/27 15:25:20 vanhu Exp $ */ /* Id: isakmp_cfg.c,v 1.55 2006/08/22 18:17:17 manubsd Exp */ /* * Copyright (C) 2004-2006 Emmanuel Dreyfus * 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 <utmp.h> #if defined(__APPLE__) && defined(__MACH__) #include <util.h> #endif #ifdef __FreeBSD__ # include <libutil.h> #endif #ifdef __NetBSD__ # include <util.h> #endif #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #if TIME_WITH_SYS_TIME # include <sys/time.h> # include <time.h> #else # if HAVE_SYS_TIME_H # include <sys/time.h> # else # include <time.h> # endif #endif #include <netdb.h> #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #if HAVE_STDINT_H #include <stdint.h> #endif #include <ctype.h> #include <resolv.h> #ifdef HAVE_LIBRADIUS #include <sys/utsname.h> #include <radlib.h> #endif #include "var.h" #include "misc.h" #include "vmbuf.h" #include "plog.h" #include "sockmisc.h" #include "schedule.h" #include "debug.h" #include "isakmp_var.h" #include "isakmp.h" #include "handler.h" #include "evt.h" #include "throttle.h" #include "remoteconf.h" #include "crypto_openssl.h" #include "isakmp_inf.h" #include "isakmp_xauth.h" #include "isakmp_unity.h" #include "isakmp_cfg.h" #include "strnames.h" #include "admin.h" #include "privsep.h" struct isakmp_cfg_config isakmp_cfg_config; static vchar_t *buffer_cat(vchar_t *s, vchar_t *append); static vchar_t *isakmp_cfg_net(struct ph1handle *, struct isakmp_data *); #if 0 static vchar_t *isakmp_cfg_void(struct ph1handle *, struct isakmp_data *); #endif static vchar_t *isakmp_cfg_addr4(struct ph1handle *, struct isakmp_data *, in_addr_t *); static void isakmp_cfg_getaddr4(struct isakmp_data *, struct in_addr *); static vchar_t *isakmp_cfg_addr4_list(struct ph1handle *, struct isakmp_data *, in_addr_t *, int); static void isakmp_cfg_appendaddr4(struct isakmp_data *, struct in_addr *, int *, int); static void isakmp_cfg_getstring(struct isakmp_data *,char *); void isakmp_cfg_iplist_to_str(char *, int, void *, int); #define ISAKMP_CFG_LOGIN 1 #define ISAKMP_CFG_LOGOUT 2 static int isakmp_cfg_accounting(struct ph1handle *, int); #ifdef HAVE_LIBRADIUS static int isakmp_cfg_accounting_radius(struct ph1handle *, int); #endif /* * Handle an ISAKMP config mode packet * We expect HDR, HASH, ATTR */ void isakmp_cfg_r(iph1, msg) struct ph1handle *iph1; vchar_t *msg; { struct isakmp *packet; struct isakmp_gen *ph; int tlen; char *npp; int np; vchar_t *dmsg; struct isakmp_ivm *ivm; /* Check that the packet is long enough to have a header */ if (msg->l < sizeof(*packet)) { plog(LLV_ERROR, LOCATION, NULL, "Unexpected short packet\n"); return; } packet = (struct isakmp *)msg->v; /* Is it encrypted? It should be encrypted */ if ((packet->flags & ISAKMP_FLAG_E) == 0) { plog(LLV_ERROR, LOCATION, NULL, "User credentials sent in cleartext!\n"); return; } /* * Decrypt the packet. If this is the beginning of a new * exchange, reinitialize the IV */ if (iph1->mode_cfg->ivm == NULL || iph1->mode_cfg->last_msgid != packet->msgid ) iph1->mode_cfg->ivm = isakmp_cfg_newiv(iph1, packet->msgid); ivm = iph1->mode_cfg->ivm; dmsg = oakley_do_decrypt(iph1, msg, ivm->iv, ivm->ive); if (dmsg == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to decrypt message\n"); return; } plog(LLV_DEBUG, LOCATION, NULL, "MODE_CFG packet\n"); plogdump(LLV_DEBUG, dmsg->v, dmsg->l); /* Now work with the decrypted packet */ packet = (struct isakmp *)dmsg->v; tlen = dmsg->l - sizeof(*packet); ph = (struct isakmp_gen *)(packet + 1); np = packet->np; while ((tlen > 0) && (np != ISAKMP_NPTYPE_NONE)) { /* Check that the payload header fits in the packet */ if (tlen < sizeof(*ph)) { plog(LLV_WARNING, LOCATION, NULL, "Short payload header\n"); goto out; } /* Check that the payload fits in the packet */ if (tlen < ntohs(ph->len)) { plog(LLV_WARNING, LOCATION, NULL, "Short payload\n"); goto out; } plog(LLV_DEBUG, LOCATION, NULL, "Seen payload %d\n", np); plogdump(LLV_DEBUG, ph, ntohs(ph->len)); switch(np) { case ISAKMP_NPTYPE_HASH: { vchar_t *check; vchar_t *payload; size_t plen; struct isakmp_gen *nph; plen = ntohs(ph->len); nph = (struct isakmp_gen *)((char *)ph + plen); plen = ntohs(nph->len); if ((payload = vmalloc(plen)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); goto out; } memcpy(payload->v, nph, plen); if ((check = oakley_compute_hash1(iph1, packet->msgid, payload)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot compute hash\n"); vfree(payload); goto out; } if (memcmp(ph + 1, check->v, check->l) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Hash verification failed\n"); vfree(payload); vfree(check); goto out; } vfree(payload); vfree(check); break; } case ISAKMP_NPTYPE_ATTR: { struct isakmp_pl_attr *attrpl; attrpl = (struct isakmp_pl_attr *)ph; isakmp_cfg_attr_r(iph1, packet->msgid, attrpl); break; } default: plog(LLV_WARNING, LOCATION, NULL, "Unexpected next payload %d\n", np); /* Skip to the next payload */ break; } /* Move to the next payload */ np = ph->np; tlen -= ntohs(ph->len); npp = (char *)ph; ph = (struct isakmp_gen *)(npp + ntohs(ph->len)); } out: vfree(dmsg); } int isakmp_cfg_attr_r(iph1, msgid, attrpl) struct ph1handle *iph1; u_int32_t msgid; struct isakmp_pl_attr *attrpl; { int type = attrpl->type; plog(LLV_DEBUG, LOCATION, NULL, "Configuration exchange type %s\n", s_isakmp_cfg_ptype(type)); switch (type) { case ISAKMP_CFG_ACK: /* ignore, but this is the time to reinit the IV */ oakley_delivm(iph1->mode_cfg->ivm); iph1->mode_cfg->ivm = NULL; return 0; break; case ISAKMP_CFG_REPLY: return isakmp_cfg_reply(iph1, attrpl); break; case ISAKMP_CFG_REQUEST: iph1->msgid = msgid; return isakmp_cfg_request(iph1, attrpl); break; case ISAKMP_CFG_SET: iph1->msgid = msgid; return isakmp_cfg_set(iph1, attrpl); break; default: plog(LLV_WARNING, LOCATION, NULL, "Unepected configuration exchange type %d\n", type); return -1; break; } return 0; } int isakmp_cfg_reply(iph1, attrpl) struct ph1handle *iph1; struct isakmp_pl_attr *attrpl; { struct isakmp_data *attr; int tlen; size_t alen; char *npp; int type; struct sockaddr_in *sin; int error; tlen = ntohs(attrpl->h.len); attr = (struct isakmp_data *)(attrpl + 1); tlen -= sizeof(*attrpl); while (tlen > 0) { type = ntohs(attr->type); /* Handle short attributes */ if ((type & ISAKMP_GEN_MASK) == ISAKMP_GEN_TV) { type &= ~ISAKMP_GEN_MASK; plog(LLV_DEBUG, LOCATION, NULL, "Short attribute %s = %d\n", s_isakmp_cfg_type(type), ntohs(attr->lorv)); switch (type) { case XAUTH_TYPE: if ((error = xauth_attr_reply(iph1, attr, ntohs(attrpl->id))) != 0) return error; break; default: plog(LLV_WARNING, LOCATION, NULL, "Ignored short attribute %s\n", s_isakmp_cfg_type(type)); break; } tlen -= sizeof(*attr); attr++; continue; } type = ntohs(attr->type); alen = ntohs(attr->lorv); /* Check that the attribute fit in the packet */ if (tlen < alen) { plog(LLV_ERROR, LOCATION, NULL, "Short attribute %s\n", s_isakmp_cfg_type(type)); return -1; } plog(LLV_DEBUG, LOCATION, NULL, "Attribute %s, len %zu\n", s_isakmp_cfg_type(type), alen); switch(type) { case XAUTH_TYPE: case XAUTH_USER_NAME: case XAUTH_USER_PASSWORD: case XAUTH_PASSCODE: case XAUTH_MESSAGE: case XAUTH_CHALLENGE: case XAUTH_DOMAIN: case XAUTH_STATUS: case XAUTH_NEXT_PIN: case XAUTH_ANSWER: if ((error = xauth_attr_reply(iph1, attr, ntohs(attrpl->id))) != 0) return error; break; case INTERNAL_IP4_ADDRESS: isakmp_cfg_getaddr4(attr, &iph1->mode_cfg->addr4); iph1->mode_cfg->flags |= ISAKMP_CFG_GOT_ADDR4; break; case INTERNAL_IP4_NETMASK: isakmp_cfg_getaddr4(attr, &iph1->mode_cfg->mask4); iph1->mode_cfg->flags |= ISAKMP_CFG_GOT_MASK4; break; case INTERNAL_IP4_DNS: isakmp_cfg_appendaddr4(attr, &iph1->mode_cfg->dns4[iph1->mode_cfg->dns4_index], &iph1->mode_cfg->dns4_index, MAXNS); iph1->mode_cfg->flags |= ISAKMP_CFG_GOT_DNS4; break; case INTERNAL_IP4_NBNS: isakmp_cfg_appendaddr4(attr, &iph1->mode_cfg->wins4[iph1->mode_cfg->wins4_index], &iph1->mode_cfg->wins4_index, MAXNS); iph1->mode_cfg->flags |= ISAKMP_CFG_GOT_WINS4; break; case UNITY_DEF_DOMAIN: isakmp_cfg_getstring(attr, iph1->mode_cfg->default_domain); iph1->mode_cfg->flags |= ISAKMP_CFG_GOT_DEFAULT_DOMAIN; break; case UNITY_SPLIT_INCLUDE: case UNITY_LOCAL_LAN: case UNITY_SPLITDNS_NAME: case UNITY_BANNER: case UNITY_SAVE_PASSWD: case UNITY_NATT_PORT: case UNITY_PFS: case UNITY_FW_TYPE: case UNITY_BACKUP_SERVERS: case UNITY_DDNS_HOSTNAME: isakmp_unity_reply(iph1, attr); break; case INTERNAL_IP4_SUBNET: case INTERNAL_ADDRESS_EXPIRY: default: plog(LLV_WARNING, LOCATION, NULL, "Ignored attribute %s\n", s_isakmp_cfg_type(type)); break; } npp = (char *)attr; attr = (struct isakmp_data *)(npp + sizeof(*attr) + alen); tlen -= (sizeof(*attr) + alen); } /* * Call the SA up script hook now that we have the configuration * It is done at the end of phase 1 if ISAKMP mode config is not * requested. */ if ((iph1->status == PHASE1ST_ESTABLISHED) && iph1->rmconf->mode_cfg) { switch (AUTHMETHOD(iph1)) { case FICTIVE_AUTH_METHOD_XAUTH_PSKEY_I: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I: /* Unimplemented */ case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_I: script_hook(iph1, SCRIPT_PHASE1_UP); break; default: break; } } #ifdef ENABLE_ADMINPORT { vchar_t *buf; alen = ntohs(attrpl->h.len) - sizeof(*attrpl); if ((buf = vmalloc(alen)) == NULL) { plog(LLV_WARNING, LOCATION, NULL, "Cannot allocate memory: %s\n", strerror(errno)); } else { memcpy(buf->v, attrpl + 1, buf->l); EVT_PUSH(iph1->local, iph1->remote, EVTT_ISAKMP_CFG_DONE, buf); vfree(buf); } } #endif return 0; } int isakmp_cfg_request(iph1, attrpl) struct ph1handle *iph1; struct isakmp_pl_attr *attrpl; { struct isakmp_data *attr; int tlen; size_t alen; char *npp; vchar_t *payload; struct isakmp_pl_attr *reply; vchar_t *reply_attr; int type; int error = -1; if ((payload = vmalloc(sizeof(*reply))) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); return -1; } memset(payload->v, 0, sizeof(*reply)); tlen = ntohs(attrpl->h.len); attr = (struct isakmp_data *)(attrpl + 1); tlen -= sizeof(*attrpl); while (tlen > 0) { reply_attr = NULL; type = ntohs(attr->type); /* Handle short attributes */ if ((type & ISAKMP_GEN_MASK) == ISAKMP_GEN_TV) { type &= ~ISAKMP_GEN_MASK; plog(LLV_DEBUG, LOCATION, NULL, "Short attribute %s = %d\n", s_isakmp_cfg_type(type), ntohs(attr->lorv)); switch (type) { case XAUTH_TYPE: reply_attr = isakmp_xauth_req(iph1, attr); break; default: plog(LLV_WARNING, LOCATION, NULL, "Ignored short attribute %s\n", s_isakmp_cfg_type(type)); break; } tlen -= sizeof(*attr); attr++; if (reply_attr != NULL) { payload = buffer_cat(payload, reply_attr); vfree(reply_attr); } continue; } type = ntohs(attr->type); alen = ntohs(attr->lorv); /* Check that the attribute fit in the packet */ if (tlen < alen) { plog(LLV_ERROR, LOCATION, NULL, "Short attribute %s\n", s_isakmp_cfg_type(type)); goto end; } plog(LLV_DEBUG, LOCATION, NULL, "Attribute %s, len %zu\n", s_isakmp_cfg_type(type), alen); switch(type) { case INTERNAL_IP4_ADDRESS: case INTERNAL_IP4_NETMASK: case INTERNAL_IP4_DNS: case INTERNAL_IP4_NBNS: case INTERNAL_IP4_SUBNET: reply_attr = isakmp_cfg_net(iph1, attr); break; case XAUTH_TYPE: case XAUTH_USER_NAME: case XAUTH_USER_PASSWORD: case XAUTH_PASSCODE: case XAUTH_MESSAGE: case XAUTH_CHALLENGE: case XAUTH_DOMAIN: case XAUTH_STATUS: case XAUTH_NEXT_PIN: case XAUTH_ANSWER: reply_attr = isakmp_xauth_req(iph1, attr); break; case APPLICATION_VERSION: reply_attr = isakmp_cfg_string(iph1, attr, ISAKMP_CFG_RACOON_VERSION); break; case UNITY_BANNER: case UNITY_PFS: case UNITY_SAVE_PASSWD: case UNITY_DEF_DOMAIN: case UNITY_DDNS_HOSTNAME: case UNITY_FW_TYPE: case UNITY_SPLITDNS_NAME: case UNITY_SPLIT_INCLUDE: case UNITY_LOCAL_LAN: case UNITY_NATT_PORT: case UNITY_BACKUP_SERVERS: reply_attr = isakmp_unity_req(iph1, attr); break; case INTERNAL_ADDRESS_EXPIRY: default: plog(LLV_WARNING, LOCATION, NULL, "Ignored attribute %s\n", s_isakmp_cfg_type(type)); break; } npp = (char *)attr; attr = (struct isakmp_data *)(npp + sizeof(*attr) + alen); tlen -= (sizeof(*attr) + alen); if (reply_attr != NULL) { payload = buffer_cat(payload, reply_attr); vfree(reply_attr); } } reply = (struct isakmp_pl_attr *)payload->v; reply->h.len = htons(payload->l); reply->type = ISAKMP_CFG_REPLY; reply->id = attrpl->id; plog(LLV_DEBUG, LOCATION, NULL, "Sending MODE_CFG REPLY\n"); error = isakmp_cfg_send(iph1, payload, ISAKMP_NPTYPE_ATTR, ISAKMP_FLAG_E, 0); if (iph1->status == PHASE1ST_ESTABLISHED) { switch (AUTHMETHOD(iph1)) { case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R: /* Unimplemented */ case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_R: script_hook(iph1, SCRIPT_PHASE1_UP); break; default: break; } } end: vfree(payload); return error; } int isakmp_cfg_set(iph1, attrpl) struct ph1handle *iph1; struct isakmp_pl_attr *attrpl; { struct isakmp_data *attr; int tlen; size_t alen; char *npp; vchar_t *payload; struct isakmp_pl_attr *reply; vchar_t *reply_attr; int type; int error = -1; if ((payload = vmalloc(sizeof(*reply))) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); return -1; } memset(payload->v, 0, sizeof(*reply)); tlen = ntohs(attrpl->h.len); attr = (struct isakmp_data *)(attrpl + 1); tlen -= sizeof(*attrpl); /* * We should send ack for the attributes we accepted */ while (tlen > 0) { reply_attr = NULL; type = ntohs(attr->type); plog(LLV_DEBUG, LOCATION, NULL, "Attribute %s\n", s_isakmp_cfg_type(type & ~ISAKMP_GEN_MASK)); switch (type & ~ISAKMP_GEN_MASK) { case XAUTH_STATUS: reply_attr = isakmp_xauth_set(iph1, attr); break; default: plog(LLV_DEBUG, LOCATION, NULL, "Unexpected SET attribute %s\n", s_isakmp_cfg_type(type & ~ISAKMP_GEN_MASK)); break; } if (reply_attr != NULL) { payload = buffer_cat(payload, reply_attr); vfree(reply_attr); } /* * Move to next attribute. If we run out of the packet, * tlen becomes negative and we exit. */ if ((type & ISAKMP_GEN_MASK) == ISAKMP_GEN_TV) { tlen -= sizeof(*attr); attr++; } else { alen = ntohs(attr->lorv); tlen -= (sizeof(*attr) + alen); npp = (char *)attr; attr = (struct isakmp_data *) (npp + sizeof(*attr) + alen); } } reply = (struct isakmp_pl_attr *)payload->v; reply->h.len = htons(payload->l); reply->type = ISAKMP_CFG_ACK; reply->id = attrpl->id; plog(LLV_DEBUG, LOCATION, NULL, "Sending MODE_CFG ACK\n"); error = isakmp_cfg_send(iph1, payload, ISAKMP_NPTYPE_ATTR, ISAKMP_FLAG_E, 0); if (iph1->mode_cfg->flags & ISAKMP_CFG_DELETE_PH1) { if (iph1->status == PHASE1ST_ESTABLISHED) isakmp_info_send_d1(iph1); remph1(iph1); delph1(iph1); iph1 = NULL; } end: vfree(payload); /* * If required, request ISAKMP mode config information */ if ((iph1 != NULL) && (iph1->rmconf->mode_cfg) && (error == 0)) error = isakmp_cfg_getconfig(iph1); return error; } static vchar_t * buffer_cat(s, append) vchar_t *s; vchar_t *append; { vchar_t *new; new = vmalloc(s->l + append->l); if (new == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); return s; } memcpy(new->v, s->v, s->l); memcpy(new->v + s->l, append->v, append->l); vfree(s); return new; } static vchar_t * isakmp_cfg_net(iph1, attr) struct ph1handle *iph1; struct isakmp_data *attr; { int type; int confsource; in_addr_t addr4; type = ntohs(attr->type); /* * Don't give an address to a peer that did not succeed Xauth */ if (xauth_check(iph1) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Attempt to start phase config whereas Xauth failed\n"); return NULL; } confsource = isakmp_cfg_config.confsource; /* * If we have to fall back to a local * configuration source, we will jump * back to this point. */ retry_source: switch(type) { case INTERNAL_IP4_ADDRESS: switch(confsource) { #ifdef HAVE_LIBLDAP case ISAKMP_CFG_CONF_LDAP: if (iph1->mode_cfg->flags & ISAKMP_CFG_ADDR4_EXTERN) break; plog(LLV_INFO, LOCATION, NULL, "No IP from LDAP, using local pool\n"); /* FALLTHROUGH */ confsource = ISAKMP_CFG_CONF_LOCAL; goto retry_source; #endif #ifdef HAVE_LIBRADIUS case ISAKMP_CFG_CONF_RADIUS: if ((iph1->mode_cfg->flags & ISAKMP_CFG_ADDR4_EXTERN) && (iph1->mode_cfg->addr4.s_addr != htonl(-2))) /* * -2 is 255.255.255.254, RADIUS uses that * to instruct the NAS to use a local pool */ break; plog(LLV_INFO, LOCATION, NULL, "No IP from RADIUS, using local pool\n"); /* FALLTHROUGH */ confsource = ISAKMP_CFG_CONF_LOCAL; goto retry_source; #endif case ISAKMP_CFG_CONF_LOCAL: if (isakmp_cfg_getport(iph1) == -1) { plog(LLV_ERROR, LOCATION, NULL, "Port pool depleted\n"); break; } iph1->mode_cfg->addr4.s_addr = htonl(ntohl(isakmp_cfg_config.network4) + iph1->mode_cfg->port); iph1->mode_cfg->flags |= ISAKMP_CFG_ADDR4_LOCAL; break; default: plog(LLV_ERROR, LOCATION, NULL, "Unexpected confsource\n"); } if (isakmp_cfg_accounting(iph1, ISAKMP_CFG_LOGIN) != 0) plog(LLV_ERROR, LOCATION, NULL, "Accounting failed\n"); return isakmp_cfg_addr4(iph1, attr, &iph1->mode_cfg->addr4.s_addr); break; case INTERNAL_IP4_NETMASK: switch(confsource) { #ifdef HAVE_LIBLDAP case ISAKMP_CFG_CONF_LDAP: if (iph1->mode_cfg->flags & ISAKMP_CFG_MASK4_EXTERN) break; plog(LLV_INFO, LOCATION, NULL, "No mask from LDAP, using local pool\n"); /* FALLTHROUGH */ confsource = ISAKMP_CFG_CONF_LOCAL; goto retry_source; #endif #ifdef HAVE_LIBRADIUS case ISAKMP_CFG_CONF_RADIUS: if (iph1->mode_cfg->flags & ISAKMP_CFG_MASK4_EXTERN) break; plog(LLV_INFO, LOCATION, NULL, "No mask from RADIUS, using local pool\n"); /* FALLTHROUGH */ confsource = ISAKMP_CFG_CONF_LOCAL; goto retry_source; #endif case ISAKMP_CFG_CONF_LOCAL: iph1->mode_cfg->mask4.s_addr = isakmp_cfg_config.netmask4; iph1->mode_cfg->flags |= ISAKMP_CFG_MASK4_LOCAL; break; default: plog(LLV_ERROR, LOCATION, NULL, "Unexpected confsource\n"); } return isakmp_cfg_addr4(iph1, attr, &iph1->mode_cfg->mask4.s_addr); break; case INTERNAL_IP4_DNS: return isakmp_cfg_addr4_list(iph1, attr, &isakmp_cfg_config.dns4[0], isakmp_cfg_config.dns4_index); break; case INTERNAL_IP4_NBNS: return isakmp_cfg_addr4_list(iph1, attr, &isakmp_cfg_config.nbns4[0], isakmp_cfg_config.nbns4_index); break; case INTERNAL_IP4_SUBNET: return isakmp_cfg_addr4(iph1, attr, &isakmp_cfg_config.network4); break; default: plog(LLV_ERROR, LOCATION, NULL, "Unexpected type %d\n", type); break; } return NULL; } #if 0 static vchar_t * isakmp_cfg_void(iph1, attr) struct ph1handle *iph1; struct isakmp_data *attr; { vchar_t *buffer; struct isakmp_data *new; if ((buffer = vmalloc(sizeof(*attr))) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); return NULL; } new = (struct isakmp_data *)buffer->v; new->type = attr->type; new->lorv = htons(0); return buffer; } #endif vchar_t * isakmp_cfg_copy(iph1, attr) struct ph1handle *iph1; struct isakmp_data *attr; { vchar_t *buffer; size_t len = 0; if ((ntohs(attr->type) & ISAKMP_GEN_MASK) == ISAKMP_GEN_TLV) len = ntohs(attr->lorv); if ((buffer = vmalloc(sizeof(*attr) + len)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); return NULL; } memcpy(buffer->v, attr, sizeof(*attr) + ntohs(attr->lorv)); return buffer; } vchar_t * isakmp_cfg_short(iph1, attr, value) struct ph1handle *iph1; struct isakmp_data *attr; int value; { vchar_t *buffer; struct isakmp_data *new; int type; if ((buffer = vmalloc(sizeof(*attr))) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); return NULL; } new = (struct isakmp_data *)buffer->v; type = ntohs(attr->type) & ~ISAKMP_GEN_MASK; new->type = htons(type | ISAKMP_GEN_TV); new->lorv = htons(value); return buffer; } vchar_t * isakmp_cfg_varlen(iph1, attr, string, len) struct ph1handle *iph1; struct isakmp_data *attr; char *string; size_t len; { vchar_t *buffer; struct isakmp_data *new; char *data; if ((buffer = vmalloc(sizeof(*attr) + len)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); return NULL; } new = (struct isakmp_data *)buffer->v; new->type = attr->type; new->lorv = htons(len); data = (char *)(new + 1); memcpy(data, string, len); return buffer; } vchar_t * isakmp_cfg_string(iph1, attr, string) struct ph1handle *iph1; struct isakmp_data *attr; char *string; { size_t len = strlen(string); return isakmp_cfg_varlen(iph1, attr, string, len); } static vchar_t * isakmp_cfg_addr4(iph1, attr, addr) struct ph1handle *iph1; struct isakmp_data *attr; in_addr_t *addr; { vchar_t *buffer; struct isakmp_data *new; size_t len; len = sizeof(*addr); if ((buffer = vmalloc(sizeof(*attr) + len)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); return NULL; } new = (struct isakmp_data *)buffer->v; new->type = attr->type; new->lorv = htons(len); memcpy(new + 1, addr, len); return buffer; } static vchar_t * isakmp_cfg_addr4_list(iph1, attr, addr, nbr) struct ph1handle *iph1; struct isakmp_data *attr; in_addr_t *addr; int nbr; { int error = -1; vchar_t *buffer = NULL; vchar_t *bufone = NULL; struct isakmp_data *new; size_t len; int i; len = sizeof(*addr); if ((buffer = vmalloc(0)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); goto out; } for(i = 0; i < nbr; i++) { if ((bufone = vmalloc(sizeof(*attr) + len)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); goto out; } new = (struct isakmp_data *)bufone->v; new->type = attr->type; new->lorv = htons(len); memcpy(new + 1, &addr[i], len); new += (len + sizeof(*attr)); buffer = buffer_cat(buffer, bufone); vfree(bufone); } error = 0; out: if ((error != 0) && (buffer != NULL)) { vfree(buffer); buffer = NULL; } return buffer; } struct isakmp_ivm * isakmp_cfg_newiv(iph1, msgid) struct ph1handle *iph1; u_int32_t msgid; { struct isakmp_cfg_state *ics = iph1->mode_cfg; if (ics == NULL) { plog(LLV_ERROR, LOCATION, NULL, "isakmp_cfg_newiv called without mode config state\n"); return NULL; } if (ics->ivm != NULL) oakley_delivm(ics->ivm); ics->ivm = oakley_newiv2(iph1, msgid); ics->last_msgid = msgid; return ics->ivm; } /* Derived from isakmp_info_send_common */ int isakmp_cfg_send(iph1, payload, np, flags, new_exchange) struct ph1handle *iph1; vchar_t *payload; u_int32_t np; int flags; int new_exchange; { struct ph2handle *iph2 = NULL; vchar_t *hash = NULL; struct isakmp *isakmp; struct isakmp_gen *gen; char *p; int tlen; int error = -1; struct isakmp_cfg_state *ics = iph1->mode_cfg; /* Check if phase 1 is established */ if ((iph1->status != PHASE1ST_ESTABLISHED) || (iph1->local == NULL) || (iph1->remote == NULL)) { plog(LLV_ERROR, LOCATION, NULL, "ISAKMP mode config exchange with immature phase 1\n"); goto end; } /* add new entry to isakmp status table */ iph2 = newph2(); if (iph2 == NULL) goto end; iph2->dst = dupsaddr(iph1->remote); if (iph2->dst == NULL) { delph2(iph2); goto end; } iph2->src = dupsaddr(iph1->local); if (iph2->src == NULL) { delph2(iph2); goto end; } #if (!defined(ENABLE_NATT)) || (defined(BROKEN_NATT)) if (set_port(iph2->dst, 0) == NULL || set_port(iph2->src, 0) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "invalid family: %d\n", iph1->remote->sa_family); delph2(iph2); goto end; } #endif iph2->ph1 = iph1; iph2->side = INITIATOR; iph2->status = PHASE2ST_START; if (new_exchange) iph2->msgid = isakmp_newmsgid2(iph1); else iph2->msgid = iph1->msgid; /* get IV and HASH(1) if skeyid_a was generated. */ if (iph1->skeyid_a != NULL) { if (new_exchange) { if (isakmp_cfg_newiv(iph1, iph2->msgid) == NULL) { delph2(iph2); goto end; } } /* generate HASH(1) */ hash = oakley_compute_hash1(iph2->ph1, iph2->msgid, payload); if (hash == NULL) { delph2(iph2); goto end; } /* initialized total buffer length */ tlen = hash->l; tlen += sizeof(*gen); } else { /* IKE-SA is not established */ hash = NULL; /* initialized total buffer length */ tlen = 0; } if ((flags & ISAKMP_FLAG_A) == 0) iph2->flags = (hash == NULL ? 0 : ISAKMP_FLAG_E); else iph2->flags = (hash == NULL ? 0 : ISAKMP_FLAG_A); insph2(iph2); bindph12(iph1, iph2); tlen += sizeof(*isakmp) + payload->l; /* create buffer for isakmp payload */ iph2->sendbuf = vmalloc(tlen); if (iph2->sendbuf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get buffer to send.\n"); goto err; } /* create isakmp header */ isakmp = (struct isakmp *)iph2->sendbuf->v; memcpy(&isakmp->i_ck, &iph1->index.i_ck, sizeof(cookie_t)); memcpy(&isakmp->r_ck, &iph1->index.r_ck, sizeof(cookie_t)); isakmp->np = hash == NULL ? (np & 0xff) : ISAKMP_NPTYPE_HASH; isakmp->v = iph1->version; isakmp->etype = ISAKMP_ETYPE_CFG; isakmp->flags = iph2->flags; memcpy(&isakmp->msgid, &iph2->msgid, sizeof(isakmp->msgid)); isakmp->len = htonl(tlen); p = (char *)(isakmp + 1); /* create HASH payload */ if (hash != NULL) { gen = (struct isakmp_gen *)p; gen->np = np & 0xff; gen->len = htons(sizeof(*gen) + hash->l); p += sizeof(*gen); memcpy(p, hash->v, hash->l); p += hash->l; } /* add payload */ memcpy(p, payload->v, payload->l); p += payload->l; #ifdef HAVE_PRINT_ISAKMP_C isakmp_printpacket(iph2->sendbuf, iph1->local, iph1->remote, 1); #endif plog(LLV_DEBUG, LOCATION, NULL, "MODE_CFG packet to send\n"); plogdump(LLV_DEBUG, iph2->sendbuf->v, iph2->sendbuf->l); /* encoding */ if (ISSET(isakmp->flags, ISAKMP_FLAG_E)) { vchar_t *tmp; tmp = oakley_do_encrypt(iph2->ph1, iph2->sendbuf, ics->ivm->ive, ics->ivm->iv); VPTRINIT(iph2->sendbuf); if (tmp == NULL) goto err; iph2->sendbuf = tmp; } /* HDR*, HASH(1), ATTR */ if (isakmp_send(iph2->ph1, iph2->sendbuf) < 0) { VPTRINIT(iph2->sendbuf); goto err; } plog(LLV_DEBUG, LOCATION, NULL, "sendto mode config %s.\n", s_isakmp_nptype(np)); /* * XXX We might need to resend the message... */ error = 0; VPTRINIT(iph2->sendbuf); err: if (iph2->sendbuf != NULL) vfree(iph2->sendbuf); unbindph12(iph2); remph2(iph2); delph2(iph2); end: if (hash) vfree(hash); return error; } void isakmp_cfg_rmstate(iph1) struct ph1handle *iph1; { struct isakmp_cfg_state *state = iph1->mode_cfg; if (isakmp_cfg_accounting(iph1, ISAKMP_CFG_LOGOUT) != 0) plog(LLV_ERROR, LOCATION, NULL, "Accounting failed\n"); if (state->flags & ISAKMP_CFG_PORT_ALLOCATED) isakmp_cfg_putport(iph1, state->port); /* Delete the IV if it's still there */ if(iph1->mode_cfg->ivm) { oakley_delivm(iph1->mode_cfg->ivm); iph1->mode_cfg->ivm = NULL; } /* Free any allocated splitnet lists */ if(iph1->mode_cfg->split_include != NULL) splitnet_list_free(iph1->mode_cfg->split_include, &iph1->mode_cfg->include_count); if(iph1->mode_cfg->split_local != NULL) splitnet_list_free(iph1->mode_cfg->split_local, &iph1->mode_cfg->local_count); xauth_rmstate(&state->xauth); racoon_free(state); iph1->mode_cfg = NULL; return; } struct isakmp_cfg_state * isakmp_cfg_mkstate(void) { struct isakmp_cfg_state *state; if ((state = racoon_malloc(sizeof(*state))) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory for mode config state\n"); return NULL; } memset(state, 0, sizeof(*state)); return state; } int isakmp_cfg_getport(iph1) struct ph1handle *iph1; { unsigned int i; size_t size = isakmp_cfg_config.pool_size; if (iph1->mode_cfg->flags & ISAKMP_CFG_PORT_ALLOCATED) return iph1->mode_cfg->port; if (isakmp_cfg_config.port_pool == NULL) { plog(LLV_ERROR, LOCATION, NULL, "isakmp_cfg_config.port_pool == NULL\n"); return -1; } for (i = 0; i < size; i++) { if (isakmp_cfg_config.port_pool[i].used == 0) break; } if (i == size) { plog(LLV_ERROR, LOCATION, NULL, "No more addresses available\n"); return -1; } isakmp_cfg_config.port_pool[i].used = 1; plog(LLV_INFO, LOCATION, NULL, "Using port %d\n", i); iph1->mode_cfg->flags |= ISAKMP_CFG_PORT_ALLOCATED; iph1->mode_cfg->port = i; return i; } int isakmp_cfg_putport(iph1, index) struct ph1handle *iph1; unsigned int index; { if (isakmp_cfg_config.port_pool == NULL) { plog(LLV_ERROR, LOCATION, NULL, "isakmp_cfg_config.port_pool == NULL\n"); return -1; } if (isakmp_cfg_config.port_pool[index].used == 0) { plog(LLV_ERROR, LOCATION, NULL, "Attempt to release an unallocated address (port %d)\n", index); return -1; } #ifdef HAVE_LIBPAM /* Cleanup PAM status associated with the port */ if (isakmp_cfg_config.authsource == ISAKMP_CFG_AUTH_PAM) privsep_cleanup_pam(index); #endif isakmp_cfg_config.port_pool[index].used = 0; iph1->mode_cfg->flags &= ISAKMP_CFG_PORT_ALLOCATED; plog(LLV_INFO, LOCATION, NULL, "Released port %d\n", index); return 0; } #ifdef HAVE_LIBPAM void cleanup_pam(port) int port; { if (isakmp_cfg_config.port_pool[port].pam != NULL) { pam_end(isakmp_cfg_config.port_pool[port].pam, PAM_SUCCESS); isakmp_cfg_config.port_pool[port].pam = NULL; } return; } #endif /* Accounting, only for RADIUS or PAM */ static int isakmp_cfg_accounting(iph1, inout) struct ph1handle *iph1; int inout; { #ifdef HAVE_LIBPAM if (isakmp_cfg_config.accounting == ISAKMP_CFG_ACCT_PAM) return privsep_accounting_pam(iph1->mode_cfg->port, inout); #endif #ifdef HAVE_LIBRADIUS if (isakmp_cfg_config.accounting == ISAKMP_CFG_ACCT_RADIUS) return isakmp_cfg_accounting_radius(iph1, inout); #endif if (isakmp_cfg_config.accounting == ISAKMP_CFG_ACCT_SYSTEM) return privsep_accounting_system(iph1->mode_cfg->port, iph1->remote, iph1->mode_cfg->login, inout); return 0; } #ifdef HAVE_LIBPAM int isakmp_cfg_accounting_pam(port, inout) int port; int inout; { int error = 0; pam_handle_t *pam; if (isakmp_cfg_config.port_pool == NULL) { plog(LLV_ERROR, LOCATION, NULL, "isakmp_cfg_config.port_pool == NULL\n"); return -1; } pam = isakmp_cfg_config.port_pool[port].pam; if (pam == NULL) { plog(LLV_ERROR, LOCATION, NULL, "pam handle is NULL\n"); return -1; } switch (inout) { case ISAKMP_CFG_LOGIN: error = pam_open_session(pam, 0); break; case ISAKMP_CFG_LOGOUT: error = pam_close_session(pam, 0); pam_end(pam, error); isakmp_cfg_config.port_pool[port].pam = NULL; break; default: plog(LLV_ERROR, LOCATION, NULL, "Unepected inout\n"); break; } if (error != 0) { plog(LLV_ERROR, LOCATION, NULL, "pam_open_session/pam_close_session failed: %s\n", pam_strerror(pam, error)); return -1; } return 0; } #endif /* HAVE_LIBPAM */ #ifdef HAVE_LIBRADIUS static int isakmp_cfg_accounting_radius(iph1, inout) struct ph1handle *iph1; int inout; { /* For first time use, initialize Radius */ if (radius_acct_state == NULL) { if ((radius_acct_state = rad_acct_open()) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot init librradius\n"); return -1; } if (rad_config(radius_acct_state, NULL) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Cannot open librarius config file: %s\n", rad_strerror(radius_acct_state)); rad_close(radius_acct_state); radius_acct_state = NULL; return -1; } } if (rad_create_request(radius_acct_state, RAD_ACCOUNTING_REQUEST) != 0) { plog(LLV_ERROR, LOCATION, NULL, "rad_create_request failed: %s\n", rad_strerror(radius_acct_state)); return -1; } if (rad_put_string(radius_acct_state, RAD_USER_NAME, iph1->mode_cfg->login) != 0) { plog(LLV_ERROR, LOCATION, NULL, "rad_put_string failed: %s\n", rad_strerror(radius_acct_state)); return -1; } switch (inout) { case ISAKMP_CFG_LOGIN: inout = RAD_START; break; case ISAKMP_CFG_LOGOUT: inout = RAD_STOP; break; default: plog(LLV_ERROR, LOCATION, NULL, "Unepected inout\n"); break; } if (rad_put_addr(radius_acct_state, RAD_FRAMED_IP_ADDRESS, iph1->mode_cfg->addr4) != 0) { plog(LLV_ERROR, LOCATION, NULL, "rad_put_addr failed: %s\n", rad_strerror(radius_acct_state)); return -1; } if (rad_put_addr(radius_acct_state, RAD_LOGIN_IP_HOST, iph1->mode_cfg->addr4) != 0) { plog(LLV_ERROR, LOCATION, NULL, "rad_put_addr failed: %s\n", rad_strerror(radius_acct_state)); return -1; } if (rad_put_int(radius_acct_state, RAD_ACCT_STATUS_TYPE, inout) != 0) { plog(LLV_ERROR, LOCATION, NULL, "rad_put_int failed: %s\n", rad_strerror(radius_acct_state)); return -1; } if (isakmp_cfg_radius_common(radius_acct_state, iph1->mode_cfg->port) != 0) return -1; if (rad_send_request(radius_acct_state) != RAD_ACCOUNTING_RESPONSE) { plog(LLV_ERROR, LOCATION, NULL, "rad_send_request failed: %s\n", rad_strerror(radius_acct_state)); return -1; } return 0; } #endif /* HAVE_LIBRADIUS */ /* * Attributes common to all RADIUS requests */ #ifdef HAVE_LIBRADIUS int isakmp_cfg_radius_common(radius_state, port) struct rad_handle *radius_state; int port; { struct utsname name; static struct hostent *host = NULL; struct in_addr nas_addr; /* * Find our own IP by resolving our nodename */ if (host == NULL) { if (uname(&name) != 0) { plog(LLV_ERROR, LOCATION, NULL, "uname failed: %s\n", strerror(errno)); return -1; } if ((host = gethostbyname(name.nodename)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "gethostbyname failed: %s\n", strerror(errno)); return -1; } } memcpy(&nas_addr, host->h_addr, sizeof(nas_addr)); if (rad_put_addr(radius_state, RAD_NAS_IP_ADDRESS, nas_addr) != 0) { plog(LLV_ERROR, LOCATION, NULL, "rad_put_addr failed: %s\n", rad_strerror(radius_state)); return -1; } if (rad_put_int(radius_state, RAD_NAS_PORT, port) != 0) { plog(LLV_ERROR, LOCATION, NULL, "rad_put_int failed: %s\n", rad_strerror(radius_state)); return -1; } if (rad_put_int(radius_state, RAD_NAS_PORT_TYPE, RAD_VIRTUAL) != 0) { plog(LLV_ERROR, LOCATION, NULL, "rad_put_int failed: %s\n", rad_strerror(radius_state)); return -1; } if (rad_put_int(radius_state, RAD_SERVICE_TYPE, RAD_FRAMED) != 0) { plog(LLV_ERROR, LOCATION, NULL, "rad_put_int failed: %s\n", rad_strerror(radius_state)); return -1; } return 0; } #endif /* Logs the user into the utmp system files. */ int isakmp_cfg_accounting_system(port, raddr, usr, inout) int port; struct sockaddr *raddr; char *usr; int inout; { int error = 0; struct utmp ut; char term[UT_LINESIZE]; char addr[NI_MAXHOST]; if (usr == NULL || usr[0]=='\0') { plog(LLV_ERROR, LOCATION, NULL, "system accounting : no login found\n"); return -1; } sprintf(term, TERMSPEC, port); switch (inout) { case ISAKMP_CFG_LOGIN: strncpy(ut.ut_name, usr, UT_NAMESIZE); ut.ut_name[UT_NAMESIZE - 1] = '\0'; strncpy(ut.ut_line, term, UT_LINESIZE); ut.ut_line[UT_LINESIZE - 1] = '\0'; GETNAMEINFO_NULL(raddr, addr); strncpy(ut.ut_host, addr, UT_HOSTSIZE); ut.ut_host[UT_HOSTSIZE - 1] = '\0'; ut.ut_time = time(NULL); plog(LLV_INFO, LOCATION, NULL, "Accounting : '%s' logging on '%s' from %s.\n", ut.ut_name, ut.ut_line, ut.ut_host); login(&ut); break; case ISAKMP_CFG_LOGOUT: plog(LLV_INFO, LOCATION, NULL, "Accounting : '%s' unlogging from '%s'.\n", usr, term); logout(term); break; default: plog(LLV_ERROR, LOCATION, NULL, "Unepected inout\n"); break; } return 0; } int isakmp_cfg_getconfig(iph1) struct ph1handle *iph1; { vchar_t *buffer; struct isakmp_pl_attr *attrpl; struct isakmp_data *attr; size_t len; int error; int attrcount; int i; int attrlist[] = { INTERNAL_IP4_ADDRESS, INTERNAL_IP4_NETMASK, INTERNAL_IP4_DNS, INTERNAL_IP4_NBNS, UNITY_BANNER, UNITY_DEF_DOMAIN, UNITY_SPLITDNS_NAME, UNITY_SPLIT_INCLUDE, UNITY_LOCAL_LAN, APPLICATION_VERSION, }; attrcount = sizeof(attrlist) / sizeof(*attrlist); len = sizeof(*attrpl) + sizeof(*attr) * attrcount; if ((buffer = vmalloc(len)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); return -1; } attrpl = (struct isakmp_pl_attr *)buffer->v; attrpl->h.len = htons(len); attrpl->type = ISAKMP_CFG_REQUEST; attrpl->id = htons((u_int16_t)(eay_random() & 0xffff)); attr = (struct isakmp_data *)(attrpl + 1); for (i = 0; i < attrcount; i++) { attr->type = htons(attrlist[i]); attr->lorv = htons(0); attr++; } plog(LLV_DEBUG, LOCATION, NULL, "Sending MODE_CFG REQUEST\n"); error = isakmp_cfg_send(iph1, buffer, ISAKMP_NPTYPE_ATTR, ISAKMP_FLAG_E, 1); vfree(buffer); return error; } static void isakmp_cfg_getaddr4(attr, ip) struct isakmp_data *attr; struct in_addr *ip; { size_t alen = ntohs(attr->lorv); in_addr_t *addr; if (alen != sizeof(*ip)) { plog(LLV_ERROR, LOCATION, NULL, "Bad IPv4 address len\n"); return; } addr = (in_addr_t *)(attr + 1); ip->s_addr = *addr; return; } static void isakmp_cfg_appendaddr4(attr, ip, num, max) struct isakmp_data *attr; struct in_addr *ip; int *num; int max; { size_t alen = ntohs(attr->lorv); in_addr_t *addr; if (alen != sizeof(*ip)) { plog(LLV_ERROR, LOCATION, NULL, "Bad IPv4 address len\n"); return; } if (*num == max) { plog(LLV_ERROR, LOCATION, NULL, "Too many addresses given\n"); return; } addr = (in_addr_t *)(attr + 1); ip->s_addr = *addr; (*num)++; return; } static void isakmp_cfg_getstring(attr, str) struct isakmp_data *attr; char *str; { size_t alen = ntohs(attr->lorv); char *src; src = (char *)(attr + 1); memcpy(str, src, (alen > MAXPATHLEN ? MAXPATHLEN : alen)); return; } #define IP_MAX 40 void isakmp_cfg_iplist_to_str(dest, count, addr, withmask) char *dest; int count; void *addr; int withmask; { int i; int p; int l; struct unity_network tmp; for(i = 0, p = 0; i < count; i++) { if(withmask == 1) l = sizeof(struct unity_network); else l = sizeof(struct in_addr); memcpy(&tmp, addr, l); addr += l; if((uint32_t)tmp.addr4.s_addr == 0) break; inet_ntop(AF_INET, &tmp.addr4, dest + p, IP_MAX); p += strlen(dest + p); if(withmask == 1) { dest[p] = '/'; p++; inet_ntop(AF_INET, &tmp.mask4, dest + p, IP_MAX); p += strlen(dest + p); } dest[p] = ' '; p++; } if(p > 0) dest[p-1] = '\0'; else dest[0] = '\0'; } int isakmp_cfg_setenv(iph1, envp, envc) struct ph1handle *iph1; char ***envp; int *envc; { char addrstr[IP_MAX]; char addrlist[IP_MAX * MAXNS + MAXNS]; char *splitlist = addrlist; char defdom[MAXPATHLEN + 1]; int cidr, tmp; char cidrstr[4]; int i, p; int test; plog(LLV_DEBUG, LOCATION, NULL, "Starting a script.\n"); /* * Internal IPv4 address, either if * we are a client or a server. */ if ((iph1->mode_cfg->flags & ISAKMP_CFG_GOT_ADDR4) || #ifdef HAVE_LIBLDAP (iph1->mode_cfg->flags & ISAKMP_CFG_ADDR4_EXTERN) || #endif #ifdef HAVE_LIBRADIUS (iph1->mode_cfg->flags & ISAKMP_CFG_ADDR4_EXTERN) || #endif (iph1->mode_cfg->flags & ISAKMP_CFG_ADDR4_LOCAL)) { inet_ntop(AF_INET, &iph1->mode_cfg->addr4, addrstr, IP_MAX); } else addrstr[0] = '\0'; if (script_env_append(envp, envc, "INTERNAL_ADDR4", addrstr) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Cannot set INTERNAL_ADDR4\n"); return -1; } if (iph1->mode_cfg->xauth.authdata.generic.usr != NULL) { if (script_env_append(envp, envc, "XAUTH_USER", iph1->mode_cfg->xauth.authdata.generic.usr) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Cannot set XAUTH_USER\n"); return -1; } } /* Internal IPv4 mask */ if (iph1->mode_cfg->flags & ISAKMP_CFG_GOT_MASK4) inet_ntop(AF_INET, &iph1->mode_cfg->mask4, addrstr, IP_MAX); else addrstr[0] = '\0'; /* * During several releases, documentation adverised INTERNAL_NETMASK4 * while code was using INTERNAL_MASK4. We now do both. */ if (script_env_append(envp, envc, "INTERNAL_MASK4", addrstr) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Cannot set INTERNAL_MASK4\n"); return -1; } if (script_env_append(envp, envc, "INTERNAL_NETMASK4", addrstr) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Cannot set INTERNAL_NETMASK4\n"); return -1; } tmp = ntohl(iph1->mode_cfg->mask4.s_addr); for (cidr = 0; tmp != 0; cidr++) tmp <<= 1; snprintf(cidrstr, 3, "%d", cidr); if (script_env_append(envp, envc, "INTERNAL_CIDR4", cidrstr) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Cannot set INTERNAL_CIDR4\n"); return -1; } /* Internal IPv4 DNS */ if (iph1->mode_cfg->flags & ISAKMP_CFG_GOT_DNS4) { /* First Internal IPv4 DNS (for compatibilty with older code */ inet_ntop(AF_INET, &iph1->mode_cfg->dns4[0], addrstr, IP_MAX); /* Internal IPv4 DNS - all */ isakmp_cfg_iplist_to_str(addrlist, iph1->mode_cfg->dns4_index, (void *)iph1->mode_cfg->dns4, 0); } else { addrstr[0] = '\0'; addrlist[0] = '\0'; } if (script_env_append(envp, envc, "INTERNAL_DNS4", addrstr) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Cannot set INTERNAL_DNS4\n"); return -1; } if (script_env_append(envp, envc, "INTERNAL_DNS4_LIST", addrlist) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Cannot set INTERNAL_DNS4_LIST\n"); return -1; } /* Internal IPv4 WINS */ if (iph1->mode_cfg->flags & ISAKMP_CFG_GOT_WINS4) { /* * First Internal IPv4 WINS * (for compatibilty with older code */ inet_ntop(AF_INET, &iph1->mode_cfg->wins4[0], addrstr, IP_MAX); /* Internal IPv4 WINS - all */ isakmp_cfg_iplist_to_str(addrlist, iph1->mode_cfg->wins4_index, (void *)iph1->mode_cfg->wins4, 0); } else { addrstr[0] = '\0'; addrlist[0] = '\0'; } if (script_env_append(envp, envc, "INTERNAL_WINS4", addrstr) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Cannot set INTERNAL_WINS4\n"); return -1; } if (script_env_append(envp, envc, "INTERNAL_WINS4_LIST", addrlist) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Cannot set INTERNAL_WINS4_LIST\n"); return -1; } /* Deault domain */ if(iph1->mode_cfg->flags & ISAKMP_CFG_GOT_DEFAULT_DOMAIN) strncpy(defdom, iph1->mode_cfg->default_domain, MAXPATHLEN + 1); else defdom[0] = '\0'; if (script_env_append(envp, envc, "DEFAULT_DOMAIN", defdom) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Cannot set DEFAULT_DOMAIN\n"); return -1; } /* Split networks */ if (iph1->mode_cfg->flags & ISAKMP_CFG_GOT_SPLIT_INCLUDE) splitlist = splitnet_list_2str(iph1->mode_cfg->split_include); else { splitlist = addrlist; addrlist[0] = '\0'; } if (script_env_append(envp, envc, "SPLIT_INCLUDE", splitlist) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Cannot set SPLIT_INCLUDE\n"); return -1; } if (splitlist != addrlist) racoon_free(splitlist); if (iph1->mode_cfg->flags & ISAKMP_CFG_GOT_SPLIT_LOCAL) splitlist = splitnet_list_2str(iph1->mode_cfg->split_local); else { splitlist = addrlist; addrlist[0] = '\0'; } if (script_env_append(envp, envc, "SPLIT_LOCAL", splitlist) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Cannot set SPLIT_LOCAL\n"); return -1; } if (splitlist != addrlist) racoon_free(splitlist); return 0; } int isakmp_cfg_resize_pool(size) int size; { struct isakmp_cfg_port *new_pool; size_t len; int i; if (size == isakmp_cfg_config.pool_size) return 0; plog(LLV_INFO, LOCATION, NULL, "Resize address pool from %zu to %d\n", isakmp_cfg_config.pool_size, size); /* If a pool already exists, check if we can shrink it */ if ((isakmp_cfg_config.port_pool != NULL) && (size < isakmp_cfg_config.pool_size)) { for (i = isakmp_cfg_config.pool_size-1; i >= size; --i) { if (isakmp_cfg_config.port_pool[i].used) { plog(LLV_ERROR, LOCATION, NULL, "resize pool from %zu to %d impossible " "port %d is in use\n", isakmp_cfg_config.pool_size, size, i); size = i; break; } } } len = size * sizeof(*isakmp_cfg_config.port_pool); new_pool = racoon_realloc(isakmp_cfg_config.port_pool, len); if (new_pool == NULL) { plog(LLV_ERROR, LOCATION, NULL, "resize pool from %zu to %d impossible: %s", isakmp_cfg_config.pool_size, size, strerror(errno)); return -1; } /* If size increase, intialize correctly the new records */ if (size > isakmp_cfg_config.pool_size) { size_t unit; size_t old_size; unit = sizeof(*isakmp_cfg_config.port_pool); old_size = isakmp_cfg_config.pool_size; bzero((char *)new_pool + (old_size * unit), (size - old_size) * unit); } isakmp_cfg_config.port_pool = new_pool; isakmp_cfg_config.pool_size = size; return 0; } int isakmp_cfg_init(cold) int cold; { int i; int error; isakmp_cfg_config.network4 = (in_addr_t)0x00000000; isakmp_cfg_config.netmask4 = (in_addr_t)0x00000000; for (i = 0; i < MAXNS; i++) isakmp_cfg_config.dns4[i] = (in_addr_t)0x00000000; isakmp_cfg_config.dns4_index = 0; for (i = 0; i < MAXWINS; i++) isakmp_cfg_config.nbns4[i] = (in_addr_t)0x00000000; isakmp_cfg_config.nbns4_index = 0; if (cold == ISAKMP_CFG_INIT_COLD) isakmp_cfg_config.port_pool = NULL; isakmp_cfg_config.authsource = ISAKMP_CFG_AUTH_SYSTEM; isakmp_cfg_config.groupsource = ISAKMP_CFG_GROUP_SYSTEM; if (cold == ISAKMP_CFG_INIT_COLD) { if (isakmp_cfg_config.grouplist != NULL) { for (i = 0; i < isakmp_cfg_config.groupcount; i++) racoon_free(isakmp_cfg_config.grouplist[i]); racoon_free(isakmp_cfg_config.grouplist); } } isakmp_cfg_config.grouplist = NULL; isakmp_cfg_config.groupcount = 0; isakmp_cfg_config.confsource = ISAKMP_CFG_CONF_LOCAL; isakmp_cfg_config.accounting = ISAKMP_CFG_ACCT_NONE; if (cold == ISAKMP_CFG_INIT_COLD) isakmp_cfg_config.pool_size = 0; isakmp_cfg_config.auth_throttle = THROTTLE_PENALTY; strlcpy(isakmp_cfg_config.default_domain, ISAKMP_CFG_DEFAULT_DOMAIN, MAXPATHLEN); strlcpy(isakmp_cfg_config.motd, ISAKMP_CFG_MOTD, MAXPATHLEN); if (cold != ISAKMP_CFG_INIT_COLD ) if (isakmp_cfg_config.splitnet_list != NULL) splitnet_list_free(isakmp_cfg_config.splitnet_list, &isakmp_cfg_config.splitnet_count); isakmp_cfg_config.splitnet_list = NULL; isakmp_cfg_config.splitnet_count = 0; isakmp_cfg_config.splitnet_type = 0; isakmp_cfg_config.pfs_group = 0; isakmp_cfg_config.save_passwd = 0; if (cold != ISAKMP_CFG_INIT_COLD ) if (isakmp_cfg_config.splitdns_list != NULL) racoon_free(isakmp_cfg_config.splitdns_list); isakmp_cfg_config.splitdns_list = NULL; isakmp_cfg_config.splitdns_len = 0; #if 0 if (cold == ISAKMP_CFG_INIT_COLD) { if ((error = isakmp_cfg_resize_pool(ISAKMP_CFG_MAX_CNX)) != 0) return error; } #endif return 0; }