diff options
Diffstat (limited to 'ipsec-tools/src/racoon/isakmp_cfg.c')
-rw-r--r-- | ipsec-tools/src/racoon/isakmp_cfg.c | 2193 |
1 files changed, 2193 insertions, 0 deletions
diff --git a/ipsec-tools/src/racoon/isakmp_cfg.c b/ipsec-tools/src/racoon/isakmp_cfg.c new file mode 100644 index 00000000..67464590 --- /dev/null +++ b/ipsec-tools/src/racoon/isakmp_cfg.c @@ -0,0 +1,2193 @@ +/* $NetBSD: isakmp_cfg.c,v 1.24.4.1 2013/04/12 10:04:21 tteras 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 <utmpx.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 vchar_t *isakmp_cfg_addrnet4(struct ph1handle *, + struct isakmp_data *, in_addr_t *, 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 (iph1->approval->authmethod) { + case OAKLEY_ATTR_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_phase1(iph1, EVT_PHASE1_MODE_CFG, 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 (iph1->approval->authmethod) { + 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 || + iph1->status == PHASE1ST_DYING) + 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: + if(isakmp_cfg_config.splitnet_count > 0){ + return isakmp_cfg_addrnet4(iph1, attr, + &isakmp_cfg_config.splitnet_list->network.addr4.s_addr, + &isakmp_cfg_config.splitnet_list->network.mask4.s_addr); + }else{ + plog(LLV_INFO, LOCATION, NULL, + "%s requested but no splitnet in configuration\n", + s_isakmp_cfg_type(type)); + } + 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 (!len) + return NULL; + + 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_addrnet4(iph1, attr, addr, mask) + struct ph1handle *iph1; + struct isakmp_data *attr; + in_addr_t *addr; + in_addr_t *mask; +{ + vchar_t *buffer; + struct isakmp_data *new; + size_t len; + in_addr_t netbuff[2]; + + len = sizeof(netbuff); + 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); + netbuff[0]=*addr; + netbuff[1]=*mask; + memcpy(new + 1, netbuff, 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; + } + + 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(iph1, 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); + + 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; +{ + 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 utmpx ut; + char addr[NI_MAXHOST]; + + if (usr == NULL || usr[0]=='\0') { + plog(LLV_ERROR, LOCATION, NULL, + "system accounting : no login found\n"); + return -1; + } + + memset(&ut, 0, sizeof ut); + gettimeofday((struct timeval *)&ut.ut_tv, NULL); + snprintf(ut.ut_id, sizeof ut.ut_id, TERMSPEC, port); + + switch (inout) { + case ISAKMP_CFG_LOGIN: + ut.ut_type = USER_PROCESS; + strncpy(ut.ut_user, usr, sizeof ut.ut_user); + + GETNAMEINFO_NULL(raddr, addr); + strncpy(ut.ut_host, addr, sizeof ut.ut_host); + + plog(LLV_INFO, LOCATION, NULL, + "Accounting : '%s' logging on '%s' from %s.\n", + ut.ut_user, ut.ut_id, addr); + + pututxline(&ut); + + break; + case ISAKMP_CFG_LOGOUT: + ut.ut_type = DEAD_PROCESS; + + plog(LLV_INFO, LOCATION, NULL, + "Accounting : '%s' unlogging from '%s'.\n", + usr, ut.ut_id); + + pututxline(&ut); + + 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 *splitlist_cidr; + 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, NETMASK); + splitlist_cidr = + splitnet_list_2str(iph1->mode_cfg->split_include, CIDR); + } else { + splitlist = addrlist; + splitlist_cidr = 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 (script_env_append(envp, envc, + "SPLIT_INCLUDE_CIDR", splitlist_cidr) != 0) { + plog(LLV_ERROR, LOCATION, NULL, + "Cannot set SPLIT_INCLUDE_CIDR\n"); + return -1; + } + if (splitlist != addrlist) + racoon_free(splitlist); + if (splitlist_cidr != addrlist) + racoon_free(splitlist_cidr); + + if (iph1->mode_cfg->flags & ISAKMP_CFG_GOT_SPLIT_LOCAL) { + splitlist = + splitnet_list_2str(iph1->mode_cfg->split_local, NETMASK); + splitlist_cidr = + splitnet_list_2str(iph1->mode_cfg->split_local, CIDR); + } else { + splitlist = addrlist; + splitlist_cidr = 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 (script_env_append(envp, envc, + "SPLIT_LOCAL_CIDR", splitlist_cidr) != 0) { + plog(LLV_ERROR, LOCATION, NULL, + "Cannot set SPLIT_LOCAL_CIDR\n"); + return -1; + } + if (splitlist != addrlist) + racoon_free(splitlist); + if (splitlist_cidr != addrlist) + racoon_free(splitlist_cidr); + + 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; +} + |