diff options
Diffstat (limited to 'sbin/isakmpd/isakmp_cfg.c')
-rw-r--r-- | sbin/isakmpd/isakmp_cfg.c | 515 |
1 files changed, 515 insertions, 0 deletions
diff --git a/sbin/isakmpd/isakmp_cfg.c b/sbin/isakmpd/isakmp_cfg.c new file mode 100644 index 00000000000..a5ef6c3677a --- /dev/null +++ b/sbin/isakmpd/isakmp_cfg.c @@ -0,0 +1,515 @@ +/* $OpenBSD: isakmp_cfg.c,v 1.1 2001/07/01 20:43:39 niklas Exp $ */ + +/* + * Copyright (c) 2001 Niklas Hallqvist. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * This code was written under funding by Gatespace + * (http://www.gatespace.com/). + */ + +#include <stdlib.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "sysdep.h" + +#include "attribute.h" +#include "conf.h" +#include "exchange.h" +#include "hash.h" +#include "ipsec.h" +#include "isakmp_fld.h" +#include "isakmp_num.h" +#include "log.h" +#include "message.h" +#include "prf.h" +#include "sa.h" +#include "util.h" + +/* + * Validation script used to test messages for correct content of + * payloads depending on the exchange type. + */ +int16_t script_transaction[] = { + ISAKMP_PAYLOAD_ATTRIBUTE, /* Initiator -> responder. */ + EXCHANGE_SCRIPT_SWITCH, + ISAKMP_PAYLOAD_ATTRIBUTE, /* Responder -> initiator. */ + EXCHANGE_SCRIPT_END +}; + +static int decode_attribute (u_int16_t, u_int8_t *, u_int16_t, void *); +static int initiator_send_ATTR (struct message *); +static int initiator_recv_ATTR (struct message *); +static int responder_recv_ATTR (struct message *); +static int responder_send_ATTR (struct message *); + +int (*isakmp_cfg_initiator[]) (struct message *) = { + initiator_send_ATTR, + initiator_recv_ATTR +}; + +int (*isakmp_cfg_responder[]) (struct message *) = { + responder_recv_ATTR, + responder_send_ATTR +}; + +/* XXX A lot can be shared with responder_send_ATTR. */ +static int +initiator_send_ATTR (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + struct hash *hash = hash_get (isa->hash); + struct prf *prf; + size_t hashsize = hash->hashsize; + u_int8_t *hashp = 0, *attrp; + size_t attrlen; + + if (exchange->phase == 2) + { + /* We want a HASH payload to start with. XXX Share with others? */ + hashp = malloc (ISAKMP_HASH_SZ + hashsize); + if (!hashp) + { + log_error ("responder_send_ATTR: malloc (%d) failed", + ISAKMP_HASH_SZ + hashsize); + return -1; + } + if (message_add_payload (msg, ISAKMP_PAYLOAD_HASH, hashp, + ISAKMP_HASH_SZ + hashsize, 1)) + { + free (hashp); + return -1; + } + } + +#ifndef to_be_removed + attrp = 0; + attrlen = 0; +#endif + + if (exchange->phase == 2) + { + prf = prf_alloc (isa->prf_type, isa->hash, isa->skeyid_a, + isa->skeyid_len); + if (!prf) + return -1; + prf->Init (prf->prfctx); + prf->Update (prf->prfctx, exchange->message_id, + ISAKMP_HDR_MESSAGE_ID_LEN); + prf->Update (prf->prfctx, attrp, attrlen); + prf->Final (hashp + ISAKMP_GEN_SZ, prf->prfctx); + prf_free (prf); + } + return 0; +} + +static int +initiator_recv_ATTR (struct message *msg) +{ + struct exchange *exchange = msg->exchange; +#if 0 + struct payload *p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_ATTRIBUTE]); +#endif + struct payload *hashp = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]); + + if (exchange->phase == 2) + { + if (!hashp) + { + /* XXX Should another NOTIFY type be used? */ + message_drop (msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, 0, 1, 0); + log_print ("initiator_recv_ATTR: phase 2 message missing HASH"); + return -1; + } + + /* XXX Verify hash! */ + + /* Mark the HASH as handled. */ + hashp->flags |= PL_MARK; + } + + return 0; +} + +static int +responder_recv_ATTR (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct payload *attrp + = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_ATTRIBUTE]); + struct payload *hashp = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]); + struct ipsec_exch *ie = exchange->data; + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + struct prf *prf; + u_int8_t *hash, *comp_hash; + size_t hash_len; + + if (exchange->phase == 2) + { + if (!hashp) + { + /* XXX Should another NOTIFY type be used? */ + message_drop (msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, 0, 1, 0); + log_print ("responder_recv_ATTR: phase 2 message missing HASH"); + return -1; + } + + hash = hashp->p; + hash_len = GET_ISAKMP_GEN_LENGTH (hash); + comp_hash = malloc (hash_len - ISAKMP_GEN_SZ); + if (!comp_hash) + { + log_error ("responder_recv_ATTR: malloc (%d) failed", + hash_len - ISAKMP_GEN_SZ); + return -1; + } + + /* Verify hash! */ + prf = prf_alloc (isa->prf_type, isa->hash, isa->skeyid_a, + isa->skeyid_len); + if (!prf) + { + free (comp_hash); + return -1; + } + prf->Init (prf->prfctx); + prf->Update (prf->prfctx, exchange->message_id, + ISAKMP_HDR_MESSAGE_ID_LEN); + prf->Update (prf->prfctx, hash + hash_len, + msg->iov[0].iov_len - ISAKMP_HDR_SZ - hash_len); + prf->Final (comp_hash, prf->prfctx); + prf_free (prf); + if (memcmp (hash + ISAKMP_GEN_SZ, comp_hash, hash_len - ISAKMP_GEN_SZ) + != 0) + { + message_drop (msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, 0, 1, 0); + free (comp_hash); + return -1; + } + free (comp_hash); + + /* Mark the HASH as handled. */ + hashp->flags |= PL_MARK; + } + + ie->cfg_id = GET_ISAKMP_ATTRIBUTE_ID (attrp->p); + + switch (attrp->p[ISAKMP_ATTRIBUTE_TYPE_OFF]) + { + case ISAKMP_CFG_REQUEST: + attribute_map (attrp->p + ISAKMP_ATTRIBUTE_ATTRS_OFF, + GET_ISAKMP_GEN_LENGTH (attrp->p) + - ISAKMP_TRANSFORM_SA_ATTRS_OFF, decode_attribute, ie); + break; + +#ifdef notyet + case ISAKMP_CFG_SET: + break; +#endif + + default: + message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); + log_print ("responder_recv_ATTR: " + "unexpected configuration message type %d", + attrp->p[ISAKMP_ATTRIBUTE_TYPE_OFF]); + return -1; + } + + return 0; +} + +/* XXX A lot can be shared with initiator_send_ATTR. */ +static int +responder_send_ATTR (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + struct hash *hash = hash_get (isa->hash); + struct prf *prf; + size_t hashsize = hash->hashsize; + u_int8_t *hashp = 0, *attrp; + size_t attrlen, off; + struct isakmp_cfg_attr *attr; + struct sockaddr *sa; + u_int32_t value; + + if (exchange->phase == 2) + { + /* We want a HASH payload to start with. XXX Share with others? */ + hashp = malloc (ISAKMP_HASH_SZ + hashsize); + if (!hashp) + { + log_error ("responder_send_ATTR: malloc (%d) failed", + ISAKMP_HASH_SZ + hashsize); + return -1; + } + if (message_add_payload (msg, ISAKMP_PAYLOAD_HASH, hashp, + ISAKMP_HASH_SZ + hashsize, 1)) + { + free (hashp); + return -1; + } + } + + /* Compute reply attribute payload length. */ + attrlen = ISAKMP_ATTRIBUTE_SZ; + for (attr = LIST_FIRST (&ie->attrs); attr; attr = LIST_NEXT (attr, link)) + { + switch (attr->type) + { + case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS: + case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK: + case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP: + case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS: + case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS: + case ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY: + attr->length = 4; + break; + + case ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET: + attr->length = 8; + break; + + case ISAKMP_CFG_ATTR_APPLICATION_VERSION: + /* XXX So far no version identifier of isakmpd here. */ + attr->length = 0; + break; + + case ISAKMP_CFG_ATTR_SUPPORTED_ATTRIBUTES: + /* XXX 2 * 6 more when IPv6 is supported. */ + attr->length = 2 * 9; + break; + +#if 0 + case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS: + case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK: + case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP: + case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS: + case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS: + attr->length = 16; + break; + + case ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET: + attr->length = 17; + break; +#endif + + default: + attr->ignore++; + /* XXX Log! */ + } + attrlen += ISAKMP_ATTR_SZ + attr->length; + } + + attrp = malloc (attrlen); + if (!attrp) + { + log_error ("responder_send_ATTR: malloc (%d) failed", attrlen); + return -1; + } + + if (message_add_payload (msg, ISAKMP_PAYLOAD_ATTRIBUTE, attrp, attrlen, 1)) + { + free (attrp); + return -1; + } + + SET_ISAKMP_ATTRIBUTE_TYPE (attrp, ISAKMP_CFG_REPLY); + SET_ISAKMP_ATTRIBUTE_ID (attrp, ie->cfg_id); + + off = ISAKMP_ATTRIBUTE_SZ; + for (attr = LIST_FIRST (&ie->attrs); attr; + off += ISAKMP_ATTR_SZ + attr->length, attr = LIST_NEXT (attr, link)) + { + SET_ISAKMP_ATTR_TYPE (attrp + off, attr->type); + SET_ISAKMP_ATTR_LENGTH_VALUE (attrp + off, attr->length); + switch (attr->type) + { + case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS: + /* XXX The section should be tagged off the peer somehow. */ + sa = conf_get_address ("ISAKMP-cfg", "Address"); + if (!sa) + /* XXX What to do? */ + continue; + if (sa->sa_family != AF_INET) + /* XXX What to do? */ + free (sa); + continue; + + memcpy (attrp + off + ISAKMP_ATTR_VALUE_OFF, + &((struct sockaddr_in *)sa)->sin_addr, attr->length); + free (sa); + break; + + case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK: + break; + + case ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET: + break; + + case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP: + break; + + case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS: + /* XXX The section should be tagged off the peer somehow. */ + sa = conf_get_address ("ISAKMP-cfg", "Nameserver"); + if (!sa) + /* XXX What to do? */ + continue; + if (sa->sa_family != AF_INET) + /* XXX What to do? */ + free (sa); + continue; + + memcpy (attrp + off + ISAKMP_ATTR_VALUE_OFF, + &((struct sockaddr_in *)sa)->sin_addr, attr->length); + free (sa); + break; + + case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS: + /* XXX The section should be tagged off the peer somehow. */ + sa = conf_get_address ("ISAKMP-cfg", "WINS-server"); + if (!sa) + /* XXX What to do? */ + continue; + if (sa->sa_family != AF_INET) + /* XXX What to do? */ + free (sa); + continue; + + memcpy (attrp + off + ISAKMP_ATTR_VALUE_OFF, + &((struct sockaddr_in *)sa)->sin_addr, attr->length); + free (sa); + break; + + case ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY: + /* XXX The section should be tagged off the peer somehow. */ + value = conf_get_num ("ISAKMP-cfg", "Lifetime", 1200); + encode_32 (attrp + off + ISAKMP_ATTR_VALUE_OFF, value); + break; + + case ISAKMP_CFG_ATTR_APPLICATION_VERSION: + /* XXX So far no version identifier of isakmpd here. */ + break; + + case ISAKMP_CFG_ATTR_SUPPORTED_ATTRIBUTES: + break; + +#if 0 + case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS: + break; + + case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK: + break; + + case ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET: + break; + + case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP: + break; + + case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS: + break; + + case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS: + break; +#endif + + default: + } + } + + if (exchange->phase == 2) + { + prf = prf_alloc (isa->prf_type, isa->hash, isa->skeyid_a, + isa->skeyid_len); + if (!prf) + { + /* XXX Log? */ + return -1; + } + prf->Init (prf->prfctx); + prf->Update (prf->prfctx, exchange->message_id, + ISAKMP_HDR_MESSAGE_ID_LEN); + prf->Update (prf->prfctx, attrp, attrlen); + prf->Final (hashp + ISAKMP_GEN_SZ, prf->prfctx); + prf_free (prf); + } + + return 0; +} + +/* + * Decode the attribute of type TYPE with a LEN length value pointed to by + * VALUE. VIE is a pointer to the IPsec exchange context holding the + * attributes indexed by type for easy retrieval. + */ +static int +decode_attribute (u_int16_t type, u_int8_t *value, u_int16_t len, void *vie) +{ + struct ipsec_exch *ie = vie; + struct isakmp_cfg_attr *attr; + + if (type >= ISAKMP_CFG_ATTR_PRIVATE_MIN + && type <= ISAKMP_CFG_ATTR_PRIVATE_MAX) + return 0; + if (type == 0 || type >= ISAKMP_CFG_ATTR_FUTURE_MIN) + /* XXX Log! */ + return -1; + + attr = calloc (1, sizeof *attr); + if (!attr) + { + log_error ("decode_attribute: calloc (1, %d) failed", sizeof *attr); + return -1; + } + attr->type = type; + attr->length = len; + if (len) + { + attr->value = malloc (len); + if (!attr->value) + { + log_error ("decode_attribute: malloc (%d) failed", len); + free (attr); + /* Should we also deallocate all other values? */ + return -1; + } + memcpy (attr->value, value, len); + } + LIST_INSERT_HEAD (&ie->attrs, attr, link); + return 0; +} |