summaryrefslogtreecommitdiff
path: root/sbin/isakmpd/isakmp_cfg.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/isakmpd/isakmp_cfg.c')
-rw-r--r--sbin/isakmpd/isakmp_cfg.c515
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;
+}