summaryrefslogtreecommitdiff
path: root/sbin/isakmpd/ipsec.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/isakmpd/ipsec.c')
-rw-r--r--sbin/isakmpd/ipsec.c1067
1 files changed, 1067 insertions, 0 deletions
diff --git a/sbin/isakmpd/ipsec.c b/sbin/isakmpd/ipsec.c
new file mode 100644
index 00000000000..f8fe5553b86
--- /dev/null
+++ b/sbin/isakmpd/ipsec.c
@@ -0,0 +1,1067 @@
+/* $Id: ipsec.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */
+
+/*
+ * Copyright (c) 1998 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 Ericsson Radio Systems.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "attribute.h"
+#include "constants.h"
+#include "crypto.h"
+#include "dh.h"
+#include "doi.h"
+#include "exchange.h"
+#include "hash.h"
+#include "ike_auth.h"
+#include "ike_main_mode.h"
+#include "ike_quick_mode.h"
+#include "ipsec.h"
+#include "ipsec_doi.h"
+#include "isakmp.h"
+#include "log.h"
+#include "math_group.h"
+#include "message.h"
+#include "prf.h"
+#include "sa.h"
+#include "sysdep.h"
+#include "timer.h"
+#include "transport.h"
+#include "util.h"
+
+struct ipsec_decode_arg {
+ struct message *msg;
+ struct sa *sa;
+ struct proto *proto;
+};
+
+static int ipsec_debug_attribute (u_int16_t, u_int8_t *, u_int16_t, void *);
+static void ipsec_delete_spi (struct sa *, struct proto *, int);
+static u_int16_t *ipsec_exchange_script (u_int8_t);
+static void ipsec_finalize_exchange (struct message *);
+static void ipsec_free_exchange_data (void *);
+static void ipsec_free_proto_data (void *);
+static void ipsec_free_sa_data (void *);
+static struct keystate *ipsec_get_keystate (struct message *);
+static u_int8_t *ipsec_get_spi (size_t *, u_int8_t, struct message *);
+static int ipsec_initiator (struct message *);
+static int ipsec_responder (struct message *);
+static void ipsec_setup_situation (u_int8_t *);
+static size_t ipsec_situation_size (void);
+static u_int8_t ipsec_spi_size (u_int8_t);
+static int ipsec_validate_attribute (u_int16_t, u_int8_t *, u_int16_t, void *);
+static int ipsec_validate_exchange (u_int8_t);
+static int ipsec_validate_id_information (u_int8_t, u_int8_t *, u_int8_t *,
+ size_t, struct exchange *);
+static int ipsec_validate_key_information (u_int8_t *, size_t);
+static int ipsec_validate_notification (u_int16_t);
+static int ipsec_validate_proto (u_int8_t);
+static int ipsec_validate_situation (u_int8_t *, size_t *);
+static int ipsec_validate_transform_id (u_int8_t, u_int8_t);
+
+static struct doi ipsec_doi = {
+ { 0 }, IPSEC_DOI_IPSEC,
+ sizeof (struct ipsec_exch), sizeof (struct ipsec_sa),
+ sizeof (struct ipsec_proto),
+ ipsec_debug_attribute,
+ ipsec_delete_spi,
+ ipsec_exchange_script,
+ ipsec_finalize_exchange,
+ ipsec_free_exchange_data,
+ ipsec_free_proto_data,
+ ipsec_free_sa_data,
+ ipsec_get_keystate,
+ ipsec_get_spi,
+ ipsec_is_attribute_incompatible,
+ ipsec_setup_situation,
+ ipsec_situation_size,
+ ipsec_spi_size,
+ ipsec_validate_attribute,
+ ipsec_validate_exchange,
+ ipsec_validate_id_information,
+ ipsec_validate_key_information,
+ ipsec_validate_notification,
+ ipsec_validate_proto,
+ ipsec_validate_situation,
+ ipsec_validate_transform_id,
+ ipsec_initiator,
+ ipsec_responder
+};
+
+int16_t script_quick_mode[] = {
+ ISAKMP_PAYLOAD_HASH, /* Initiator -> responder. */
+ ISAKMP_PAYLOAD_SA,
+ ISAKMP_PAYLOAD_NONCE,
+ EXCHANGE_SCRIPT_SWITCH,
+ ISAKMP_PAYLOAD_HASH, /* Responder -> initiator. */
+ ISAKMP_PAYLOAD_SA,
+ ISAKMP_PAYLOAD_NONCE,
+ EXCHANGE_SCRIPT_SWITCH,
+ ISAKMP_PAYLOAD_HASH, /* Initiator -> responder. */
+ EXCHANGE_SCRIPT_END
+};
+
+int16_t script_new_group_mode[] = {
+ ISAKMP_PAYLOAD_HASH, /* Initiator -> responder. */
+ ISAKMP_PAYLOAD_SA,
+ EXCHANGE_SCRIPT_SWITCH,
+ ISAKMP_PAYLOAD_HASH, /* Responder -> initiator. */
+ ISAKMP_PAYLOAD_SA,
+ EXCHANGE_SCRIPT_END
+};
+
+static void
+ipsec_finalize_exchange (struct message *msg)
+{
+ struct sa *isakmp_sa = msg->isakmp_sa;
+ struct ipsec_sa *isa = isakmp_sa->data;
+ struct exchange *exchange = msg->exchange;
+ struct ipsec_exch *ie = exchange->data;
+ struct sa *sa;
+ struct proto *proto, *last_proto = 0;
+ int initiator = exchange->initiator;
+ struct timeval expiration;
+
+ switch (exchange->phase)
+ {
+ case 1:
+ switch (exchange->type)
+ {
+ case ISAKMP_EXCH_ID_PROT:
+ case ISAKMP_EXCH_AGGRESSIVE:
+ isa->hash = ie->hash->type;
+ isa->prf_type = ie->prf_type;
+ isa->skeyid_len = ie->skeyid_len;
+ isa->skeyid_d = ie->skeyid_d;
+ isa->skeyid_a = ie->skeyid_a;
+ /* Prevents early free of SKEYID_*. */
+ ie->skeyid_a = ie->skeyid_d = 0;
+ break;
+ }
+
+ /* If a lifetime was negotiated sutup the death timer. */
+ if (isakmp_sa->seconds)
+ {
+ gettimeofday(&expiration, 0);
+ expiration.tv_sec += isakmp_sa->seconds;
+ isakmp_sa->death = timer_add_event ("sa_rekey_p1",
+ (void (*) (void *))sa_rekey_p1,
+ isakmp_sa, &expiration);
+ if (!isakmp_sa->death)
+ {
+ /* If we don't give up we might start leaking... */
+ sa_delete (isakmp_sa, 1);
+ return;
+ }
+ }
+ break;
+
+ case 2:
+ switch (exchange->type)
+ {
+ case IKE_EXCH_QUICK_MODE:
+ /*
+ * Tell the application(s) about the SPIs and key material.
+ */
+ for (sa = TAILQ_FIRST (&exchange->sa_list); sa;
+ sa = TAILQ_NEXT (sa, next))
+ {
+ for (proto = TAILQ_FIRST (&sa->protos), last_proto = 0; proto;
+ proto = TAILQ_NEXT (proto, link))
+ {
+ if (sysdep_ipsec_set_spi (sa, proto, 0, initiator)
+ || (last_proto
+ && sysdep_ipsec_group_spis (sa, last_proto, proto,
+ 0))
+ || sysdep_ipsec_set_spi (sa, proto, 1, initiator)
+ || (last_proto
+ && sysdep_ipsec_group_spis (sa, last_proto, proto,
+ 1)))
+ /* XXX Tear down this exchange. */
+ return;
+ last_proto = proto;
+ }
+ if (sysdep_ipsec_enable_spi (sa, initiator))
+ /* XXX Tear down this exchange. */
+ return;
+ }
+ break;
+ }
+ }
+}
+
+static void
+ipsec_free_exchange_data (void *vie)
+{
+ struct ipsec_exch *ie = vie;
+
+ if (ie->sa_i_b)
+ free (ie->sa_i_b);
+ if (ie->g_xi)
+ free (ie->g_xi);
+ if (ie->g_xr)
+ free (ie->g_xr);
+ if (ie->g_xy)
+ free (ie->g_xy);
+ if (ie->skeyid)
+ free (ie->skeyid);
+ if (ie->skeyid_d)
+ free (ie->skeyid_d);
+ if (ie->skeyid_a)
+ free (ie->skeyid_a);
+ if (ie->skeyid_e)
+ free (ie->skeyid_e);
+ if (ie->hash_i)
+ free (ie->hash_i);
+ if (ie->hash_r)
+ free (ie->hash_r);
+}
+
+static void
+ipsec_free_sa_data (void *visa)
+{
+ struct ipsec_sa *isa = visa;
+
+ if (isa->skeyid_a)
+ free (isa->skeyid_a);
+ if (isa->skeyid_d)
+ free (isa->skeyid_d);
+}
+
+static void
+ipsec_free_proto_data (void *viproto)
+{
+ struct ipsec_proto *iproto = viproto;
+ int i;
+
+ for (i = 0; i < 2; i++)
+ if (iproto->keymat[i])
+ free (iproto->keymat[i]);
+}
+
+static u_int16_t *
+ipsec_exchange_script (u_int8_t type)
+{
+ switch (type)
+ {
+ case IKE_EXCH_QUICK_MODE:
+ return script_quick_mode;
+ case IKE_EXCH_NEW_GROUP_MODE:
+ return script_new_group_mode;
+ }
+ return 0;
+}
+
+/* Requires doi_init to already have been called. */
+void
+ipsec_init ()
+{
+ doi_register (&ipsec_doi);
+}
+
+/* Given a message MSG, return a suitable IV (or rather keystate). */
+static struct keystate *
+ipsec_get_keystate (struct message *msg)
+{
+ struct keystate *ks;
+ struct hash *hash;
+
+ /* If we have already have an IV, use it. */
+ if (msg->exchange && msg->exchange->keystate)
+ return msg->exchange->keystate;
+
+ /*
+ * For phase 2 when no SA yet is setup we need to hash the IV used by
+ * the ISAKMP SA concatenated with the message ID, and use that as an
+ * IV for further cryptographic operations.
+ */
+ ks = crypto_clone_keystate (msg->isakmp_sa->keystate);
+ if (!ks)
+ return 0;
+
+ hash = hash_get (((struct ipsec_sa *)msg->isakmp_sa->data)->hash);
+ hash->Init (hash->ctx);
+ log_debug_buf (LOG_CRYPTO, 80, "ipsec_get_keystate: final phase 1 IV",
+ ks->riv, ks->xf->blocksize);
+ hash->Update (hash->ctx, ks->riv, ks->xf->blocksize);
+ log_debug_buf (LOG_CRYPTO, 80, "ipsec_get_keystate: message ID",
+ ((u_int8_t *)msg->iov[0].iov_base)
+ + ISAKMP_HDR_MESSAGE_ID_OFF,
+ ISAKMP_HDR_MESSAGE_ID_LEN);
+ hash->Update (hash->ctx,
+ ((u_int8_t *)msg->iov[0].iov_base) + ISAKMP_HDR_MESSAGE_ID_OFF,
+ ISAKMP_HDR_MESSAGE_ID_LEN);
+ hash->Final (hash->digest, hash->ctx);
+ crypto_init_iv (ks, hash->digest, ks->xf->blocksize);
+ log_debug_buf (LOG_CRYPTO, 80, "ipsec_get_keystate: phase 2 IV",
+ hash->digest, ks->xf->blocksize);
+ return ks;
+}
+
+static void
+ipsec_setup_situation (u_int8_t *buf)
+{
+ SET_IPSEC_SIT_SIT (buf + ISAKMP_SA_SIT_OFF, IPSEC_SIT_IDENTITY_ONLY);
+}
+
+static size_t
+ipsec_situation_size (void)
+{
+ return IPSEC_SIT_SIT_LEN;
+}
+
+static u_int8_t
+ipsec_spi_size (u_int8_t proto)
+{
+ return IPSEC_SPI_SIZE;
+}
+
+static int
+ipsec_validate_attribute (u_int16_t type, u_int8_t *value, u_int16_t len,
+ void *vmsg)
+{
+ struct message *msg = vmsg;
+
+ if ((msg->exchange->phase == 1
+ && (type < IKE_ATTR_ENCRYPTION_ALGORITHM
+ || type > IKE_ATTR_GROUP_ORDER))
+ || (msg->exchange->phase == 2
+ && (type < IPSEC_ATTR_SA_LIFE_TYPE
+ || type > IPSEC_ATTR_COMPRESS_PRIVATE_ALGORITHM)))
+ return -1;
+ return 0;
+}
+
+static int
+ipsec_validate_exchange (u_int8_t exch)
+{
+ return exch != IKE_EXCH_QUICK_MODE && exch != IKE_EXCH_NEW_GROUP_MODE;
+}
+
+static int
+ipsec_validate_id_information (u_int8_t type, u_int8_t *extra, u_int8_t *buf,
+ size_t sz, struct exchange *exchange)
+{
+ u_int8_t proto = GET_IPSEC_ID_PROTO (extra);
+ u_int16_t port = GET_IPSEC_ID_PORT (extra);
+
+ log_debug (LOG_MESSAGE, 0,
+ "ipsec_validate_id_information: proto %d port %d type %d",
+ proto, port, type);
+ if (type < IPSEC_ID_IPV4_ADDR || type > IPSEC_ID_KEY_ID)
+ return -1;
+
+ if (exchange->phase == 1
+ && (proto != IPPROTO_UDP || port != UDP_DEFAULT_PORT)
+ && (proto != 0 || port != 0))
+ {
+/* XXX SSH's ISAKMP tester fails this test (proto 17 - port 0). */
+#ifdef notyet
+ return -1;
+#else
+ log_print ("ipsec_validate_id_information: "
+ "dubious ID information accepted");
+#endif
+ }
+
+ /* XXX More checks? */
+
+ return 0;
+}
+
+static int
+ipsec_validate_key_information (u_int8_t *buf, size_t sz)
+{
+ /* XXX Not implemented yet. */
+ return 0;
+}
+
+static int
+ipsec_validate_notification (u_int16_t type)
+{
+ return type < IPSEC_NOTIFY_RESPONDER_LIFETIME
+ || type > IPSEC_NOTIFY_INITIAL_CONTACT ? -1 : 0;
+}
+
+static int
+ipsec_validate_proto (u_int8_t proto)
+{
+ return proto < IPSEC_PROTO_IPSEC_AH || proto > IPSEC_PROTO_IPCOMP ? -1 : 0;
+}
+
+static int
+ipsec_validate_situation (u_int8_t *buf, size_t *sz)
+{
+ int sit = GET_IPSEC_SIT_SIT (buf);
+ int off;
+
+ if (sit & (IPSEC_SIT_SECRECY | IPSEC_SIT_INTEGRITY))
+ {
+ /*
+ * XXX All the roundups below, round up to 32 bit boundaries given
+ * that the situation field is aligned. This is not necessarily so,
+ * but I interpret the drafts as this is like this they want it.
+ */
+ off = ROUNDUP_32 (GET_IPSEC_SIT_SECRECY_LENGTH (buf));
+ off += ROUNDUP_32 (GET_IPSEC_SIT_SECRECY_CAT_LENGTH (buf + off));
+ off += ROUNDUP_32 (GET_IPSEC_SIT_INTEGRITY_LENGTH (buf + off));
+ off += ROUNDUP_32 (GET_IPSEC_SIT_INTEGRITY_CAT_LENGTH (buf + off));
+ *sz = off + IPSEC_SIT_SZ;
+ }
+ else
+ *sz = IPSEC_SIT_SIT_LEN;
+
+ /* Currently only "identity only" situations are supported. */
+#ifdef notdef
+ return
+ sit & ~(IPSEC_SIT_IDENTITY_ONLY | IPSEC_SIT_SECRECY | IPSEC_SIT_INTEGRITY);
+#else
+ return sit & ~IPSEC_SIT_IDENTITY_ONLY;
+#endif
+ return 1;
+ return 0;
+}
+
+static int
+ipsec_validate_transform_id (u_int8_t proto, u_int8_t transform_id)
+{
+ switch (proto)
+ {
+ /*
+ * As no unexpected protocols can occur, we just tie the default case
+ * to the first case, in orer to silence a GCC warning.
+ */
+ default:
+ case ISAKMP_PROTO_ISAKMP:
+ return transform_id != IPSEC_TRANSFORM_KEY_IKE;
+ case IPSEC_PROTO_IPSEC_AH:
+ return
+ transform_id < IPSEC_AH_MD5 || transform_id > IPSEC_AH_DES ? -1 : 0;
+ case IPSEC_PROTO_IPSEC_ESP:
+ return transform_id < IPSEC_ESP_DES_IV64
+ || transform_id > IPSEC_ESP_NULL ? -1 : 0;
+ case IPSEC_PROTO_IPCOMP:
+ return transform_id < IPSEC_IPCOMP_OUI
+ || transform_id > IPSEC_IPCOMP_V42BIS ? -1 : 0;
+ }
+}
+
+static int
+ipsec_initiator (struct message *msg)
+{
+ struct exchange *exchange = msg->exchange;
+ int (**script) (struct message *msg) = 0;
+
+ /* XXX Mostly not implemented yet. */
+
+ /* Check that the SA is coherent with the IKE rules. */
+ if ((exchange->phase == 1 && exchange->type != ISAKMP_EXCH_ID_PROT
+ && exchange->type != ISAKMP_EXCH_INFO)
+ || (exchange->phase == 2 && exchange->type == ISAKMP_EXCH_ID_PROT))
+ {
+ log_print ("ipsec_initiator: unsupported exchange type %d in phase %d",
+ exchange->type, exchange->phase);
+ return -1;
+ }
+
+ switch (exchange->type)
+ {
+ case ISAKMP_EXCH_BASE:
+ break;
+ case ISAKMP_EXCH_ID_PROT:
+ script = ike_main_mode_initiator;
+ break;
+ case ISAKMP_EXCH_AUTH_ONLY:
+ log_print ("ipsec_initiator: unuspported exchange type %d",
+ exchange->type);
+ return -1;
+ case ISAKMP_EXCH_AGGRESSIVE:
+ break;
+ case ISAKMP_EXCH_INFO:
+ message_send_info (msg);
+ break;
+ case IKE_EXCH_QUICK_MODE:
+ script = ike_quick_mode_initiator;
+ break;
+ case IKE_EXCH_NEW_GROUP_MODE:
+ break;
+ }
+
+ /* Run the script code for this step. */
+ if (script)
+ return script[exchange->step] (msg);
+
+ return 0;
+}
+
+static int
+ipsec_responder (struct message *msg)
+{
+ struct exchange *exchange = msg->exchange;
+ int (**script) (struct message *msg) = 0;
+
+ /* Check that a new exchange is coherent with the IKE rules. */
+ if (exchange->step == 0
+ && ((exchange->phase == 1 && exchange->type != ISAKMP_EXCH_ID_PROT
+ && exchange->type != ISAKMP_EXCH_INFO)
+ || (exchange->phase == 2 && exchange->type == ISAKMP_EXCH_ID_PROT)))
+ {
+ message_drop (msg, ISAKMP_NOTIFY_UNSUPPORTED_EXCHANGE_TYPE, 0, 0, 0);
+ return -1;
+ }
+
+ log_debug (LOG_MISC, 30,
+ "ipsec_responder: phase %d exchange %d step %d", exchange->phase,
+ exchange->type, exchange->step);
+ switch (exchange->type)
+ {
+ case ISAKMP_EXCH_BASE:
+ case ISAKMP_EXCH_AUTH_ONLY:
+ message_drop (msg, ISAKMP_NOTIFY_UNSUPPORTED_EXCHANGE_TYPE, 0, 0, 0);
+ return -1;
+
+ case ISAKMP_EXCH_ID_PROT:
+ script = ike_main_mode_responder;
+ break;
+
+ case ISAKMP_EXCH_AGGRESSIVE:
+ /* XXX Not implemented yet. */
+ break;
+
+ case ISAKMP_EXCH_INFO:
+ /* XXX Not implemented yet. */
+ break;
+
+ case IKE_EXCH_QUICK_MODE:
+ script = ike_quick_mode_responder;
+ break;
+
+ case IKE_EXCH_NEW_GROUP_MODE:
+ /* XXX Not implemented yet. */
+ break;
+ }
+
+ /* Run the script code for this step. */
+ if (script)
+ return script[exchange->step] (msg);
+
+ /*
+ * XXX So far we don't accept any proposals for exchanges we don't support.
+ */
+ if (TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SA]))
+ {
+ message_drop (msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 0, 0);
+ return -1;
+ }
+ return 0;
+}
+
+static enum hashes from_ike_hash (u_int16_t hash)
+{
+ switch (hash)
+ {
+ case IKE_HASH_MD5:
+ return HASH_MD5;
+ case IKE_HASH_SHA:
+ return HASH_SHA1;
+ }
+ return -1;
+}
+
+static enum transform from_ike_crypto (u_int16_t crypto)
+{
+ /* Coincidentally this is the null operation :-) */
+ return crypto;
+}
+
+int
+ipsec_is_attribute_incompatible (u_int16_t type, u_int8_t *value,
+ u_int16_t len, void *vmsg)
+{
+ struct message *msg = vmsg;
+
+ if (msg->exchange->phase == 1)
+ {
+ switch (type)
+ {
+ case IKE_ATTR_ENCRYPTION_ALGORITHM:
+ return !crypto_get (from_ike_crypto (decode_16 (value)));
+ case IKE_ATTR_HASH_ALGORITHM:
+ return !hash_get (from_ike_hash (decode_16 (value)));
+ case IKE_ATTR_AUTHENTICATION_METHOD:
+ return !ike_auth_get (decode_16 (value));
+ case IKE_ATTR_GROUP_DESCRIPTION:
+ return decode_16 (value) < IKE_GROUP_DESC_MODP_768
+ || decode_16 (value) > IKE_GROUP_DESC_EC2N_185;
+ case IKE_ATTR_GROUP_TYPE:
+ return 1;
+ case IKE_ATTR_GROUP_PRIME:
+ return 1;
+ case IKE_ATTR_GROUP_GENERATOR_1:
+ return 1;
+ case IKE_ATTR_GROUP_GENERATOR_2:
+ return 1;
+ case IKE_ATTR_GROUP_CURVE_A:
+ return 1;
+ case IKE_ATTR_GROUP_CURVE_B:
+ return 1;
+ case IKE_ATTR_LIFE_TYPE:
+ return decode_16 (value) < IKE_DURATION_SECONDS
+ || decode_16 (value) > IKE_DURATION_KILOBYTES;
+ case IKE_ATTR_LIFE_DURATION:
+ return 0;
+ case IKE_ATTR_PRF:
+ return 1;
+ case IKE_ATTR_KEY_LENGTH:
+ /*
+ * Our crypto routines only allows key-lengths which are multiples
+ * of an octet.
+ */
+ return decode_16 (value) % 8 != 0;
+ case IKE_ATTR_FIELD_SIZE:
+ return 1;
+ case IKE_ATTR_GROUP_ORDER:
+ return 1;
+ }
+ }
+ else
+ {
+ switch (type)
+ {
+ case IPSEC_ATTR_SA_LIFE_TYPE:
+ return decode_16 (value) < IPSEC_DURATION_SECONDS
+ || decode_16 (value) > IPSEC_DURATION_KILOBYTES;
+ case IPSEC_ATTR_SA_LIFE_DURATION:
+ return 0;
+ case IPSEC_ATTR_GROUP_DESCRIPTION:
+ return decode_16 (value) < IKE_GROUP_DESC_MODP_768
+ || decode_16 (value) > IKE_GROUP_DESC_EC2N_185;
+ case IPSEC_ATTR_ENCAPSULATION_MODE:
+ return decode_16 (value) < IPSEC_ENCAP_TUNNEL
+ || decode_16 (value) > IPSEC_ENCAP_TRANSPORT;
+ case IPSEC_ATTR_AUTHENTICATION_ALGORITHM:
+ return decode_16 (value) < IPSEC_AUTH_HMAC_MD5
+ || decode_16 (value) > IPSEC_AUTH_KPDK;
+ case IPSEC_ATTR_KEY_LENGTH:
+ return 1;
+ case IPSEC_ATTR_KEY_ROUNDS:
+ return 1;
+ case IPSEC_ATTR_COMPRESS_DICTIONARY_SIZE:
+ return 1;
+ case IPSEC_ATTR_COMPRESS_PRIVATE_ALGORITHM:
+ return 1;
+ }
+ }
+ /* XXX Silence gcc. */
+ return 1;
+}
+
+int
+ipsec_debug_attribute (u_int16_t type, u_int8_t *value, u_int16_t len,
+ void *vmsg)
+{
+ struct message *msg = vmsg;
+ char val[20];
+
+ /* XXX Transient solution. */
+ if (len == 2)
+ sprintf (val, "%d", decode_16 (value));
+ else if (len == 4)
+ sprintf (val, "%d", decode_32 (value));
+ else
+ sprintf (val, "unrepresentable");
+
+ log_debug (LOG_MESSAGE, 50, "Attribute %s value %s",
+ constant_name (msg->exchange->phase == 1
+ ? ike_attr_cst : ipsec_attr_cst, type),
+ val);
+ return 0;
+}
+
+int
+ipsec_decode_attribute (u_int16_t type, u_int8_t *value, u_int16_t len,
+ void *vida)
+{
+ struct ipsec_decode_arg *ida = vida;
+ struct message *msg = ida->msg;
+ struct sa *sa = ida->sa;
+ struct ipsec_sa *isa = sa->data;
+ struct proto *proto = ida->proto;
+ struct ipsec_proto *iproto = proto->data;
+ struct exchange *exchange = msg->exchange;
+ struct ipsec_exch *ie = exchange->data;
+ static int lifetype = 0;
+
+ if (exchange->phase == 1)
+ {
+ switch (type)
+ {
+ case IKE_ATTR_ENCRYPTION_ALGORITHM:
+ /* XXX Errors possible? */
+ exchange->crypto = crypto_get (from_ike_crypto (decode_16 (value)));
+ break;
+ case IKE_ATTR_HASH_ALGORITHM:
+ /* XXX Errors possible? */
+ ie->hash = hash_get (from_ike_hash (decode_16 (value)));
+ break;
+ case IKE_ATTR_AUTHENTICATION_METHOD:
+ /* XXX Errors possible? */
+ ie->ike_auth = ike_auth_get (decode_16 (value));
+ break;
+ case IKE_ATTR_GROUP_DESCRIPTION:
+ /* XXX Errors possible? */
+ ie->group = group_get (decode_16 (value));
+ break;
+ case IKE_ATTR_GROUP_TYPE:
+ break;
+ case IKE_ATTR_GROUP_PRIME:
+ break;
+ case IKE_ATTR_GROUP_GENERATOR_1:
+ break;
+ case IKE_ATTR_GROUP_GENERATOR_2:
+ break;
+ case IKE_ATTR_GROUP_CURVE_A:
+ break;
+ case IKE_ATTR_GROUP_CURVE_B:
+ break;
+ case IKE_ATTR_LIFE_TYPE:
+ lifetype = decode_16 (value);
+ return 0;
+ case IKE_ATTR_LIFE_DURATION:
+ switch (lifetype)
+ {
+ case IKE_DURATION_SECONDS:
+ switch (len)
+ {
+ case 2:
+ sa->seconds = decode_16 (value);
+ break;
+ case 4:
+ sa->seconds = decode_32 (value);
+ break;
+ default:
+ /* XXX Log. */
+ }
+ break;
+ case IKE_DURATION_KILOBYTES:
+ switch (len)
+ {
+ case 2:
+ sa->kilobytes = decode_16 (value);
+ break;
+ case 4:
+ sa->kilobytes = decode_32 (value);
+ break;
+ default:
+ /* XXX Log. */
+ }
+ break;
+ default:
+ /* XXX Log! */
+ }
+ break;
+ case IKE_ATTR_PRF:
+ break;
+ case IKE_ATTR_KEY_LENGTH:
+ exchange->key_length = decode_16 (value) / 8;
+ break;
+ case IKE_ATTR_FIELD_SIZE:
+ break;
+ case IKE_ATTR_GROUP_ORDER:
+ break;
+ }
+ }
+ else
+ {
+ switch (type)
+ {
+ case IPSEC_ATTR_SA_LIFE_TYPE:
+ lifetype = decode_16 (value);
+ return 0;
+ case IPSEC_ATTR_SA_LIFE_DURATION:
+ switch (lifetype)
+ {
+ case IPSEC_DURATION_SECONDS:
+ switch (len)
+ {
+ case 2:
+ sa->seconds = decode_16 (value);
+ break;
+ case 4:
+ sa->seconds = decode_32 (value);
+ break;
+ default:
+ /* XXX Log. */
+ }
+ break;
+ case IPSEC_DURATION_KILOBYTES:
+ switch (len)
+ {
+ case 2:
+ sa->kilobytes = decode_16 (value);
+ break;
+ case 4:
+ sa->kilobytes = decode_32 (value);
+ break;
+ default:
+ /* XXX Log. */
+ }
+ break;
+ default:
+ /* XXX Log! */
+ }
+ break;
+ case IPSEC_ATTR_GROUP_DESCRIPTION:
+ isa->group_desc = decode_16 (value);
+ break;
+ case IPSEC_ATTR_ENCAPSULATION_MODE:
+ /* XXX Multiple protocols must have same encapsulation mode, no? */
+ iproto->encap_mode = decode_16 (value);
+ break;
+ case IPSEC_ATTR_AUTHENTICATION_ALGORITHM:
+ iproto->auth = decode_16 (value);
+ break;
+ case IPSEC_ATTR_KEY_LENGTH:
+ iproto->keylen = decode_16 (value);
+ break;
+ case IPSEC_ATTR_KEY_ROUNDS:
+ iproto->keyrounds = decode_16 (value);
+ break;
+ case IPSEC_ATTR_COMPRESS_DICTIONARY_SIZE:
+ break;
+ case IPSEC_ATTR_COMPRESS_PRIVATE_ALGORITHM:
+ break;
+ }
+ }
+ lifetype = 0;
+ return 0;
+}
+
+/*
+ * Walk over the attributes of the transform payload found in BUF, and
+ * fill out the fields of the SA attached to MSG. Also mark the SA as
+ * processed.
+ */
+void
+ipsec_decode_transform (struct message *msg, struct sa *sa,
+ struct proto *proto, u_int8_t *buf)
+{
+ struct ipsec_exch *ie = msg->exchange->data;
+ struct ipsec_decode_arg ida;
+
+ log_debug (LOG_MISC, 20, "ipsec_decode_transform: transform %d chosen",
+ GET_ISAKMP_TRANSFORM_NO (buf));
+
+ ida.msg = msg;
+ ida.sa = sa;
+ ida.proto = proto;
+
+ /* The default IKE lifetime is 8 hours. */
+ if (sa->phase == 1)
+ sa->seconds = 28800;
+
+ /* Extract the attributes and stuff them into the SA. */
+ attribute_map (buf + ISAKMP_TRANSFORM_SA_ATTRS_OFF,
+ GET_ISAKMP_GEN_LENGTH (buf) - ISAKMP_TRANSFORM_SA_ATTRS_OFF,
+ ipsec_decode_attribute, &ida);
+
+ /*
+ * If no pseudo-random function was negotiated, it's HMAC.
+ * XXX As PRF_HMAC currently is zero, this is a no-op.
+ */
+ if (!ie->prf_type)
+ ie->prf_type = PRF_HMAC;
+}
+
+static void
+ipsec_delete_spi (struct sa *sa, struct proto *proto, int initiator)
+{
+ if (sa->phase == 1)
+ return;
+ /* XXX Error handling? Is it interesting? */
+ sysdep_ipsec_delete_spi (sa, proto, initiator);
+}
+
+static int
+ipsec_g_x (struct message *msg, int peer, u_int8_t *buf)
+{
+ struct exchange *exchange = msg->exchange;
+ struct ipsec_exch *ie = exchange->data;
+ u_int8_t **g_x;
+ int initiator = exchange->initiator ^ peer;
+ char header[32];
+
+ g_x = initiator ? &ie->g_xi : &ie->g_xr;
+ *g_x = malloc (ie->g_x_len);
+ if (!*g_x)
+ return -1;
+ memcpy (*g_x, buf, ie->g_x_len);
+ snprintf (header, 32, "ipsec_g_x: g^x%c", initiator ? 'i' : 'r');
+ log_debug_buf (LOG_MISC, 80, header, *g_x, ie->g_x_len);
+ return 0;
+}
+
+/* Generate our DH value. */
+int
+ipsec_gen_g_x (struct message *msg)
+{
+ struct exchange *exchange = msg->exchange;
+ struct ipsec_exch *ie = exchange->data;
+ u_int8_t *buf;
+
+ buf = malloc (ISAKMP_KE_SZ + ie->g_x_len);
+ if (!buf)
+ return -1;
+
+ if (message_add_payload (msg, ISAKMP_PAYLOAD_KEY_EXCH, buf,
+ ISAKMP_KE_SZ + ie->g_x_len, 1))
+ {
+ free (buf);
+ return -1;
+ }
+
+ dh_create_exchange (ie->group, buf + ISAKMP_KE_DATA_OFF);
+ return ipsec_g_x (msg, 0, buf + ISAKMP_KE_DATA_OFF);
+}
+
+/* Save the peer's DH value. */
+int
+ipsec_save_g_x (struct message *msg)
+{
+ struct exchange *exchange = msg->exchange;
+ struct ipsec_exch *ie = exchange->data;
+ struct payload *kep;
+
+ kep = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_KEY_EXCH]);
+ kep->flags |= PL_MARK;
+ ie->g_x_len = GET_ISAKMP_GEN_LENGTH (kep->p) - ISAKMP_KE_DATA_OFF;
+
+ /* Check that the given length matches the group's expectancy. */
+ if (ie->g_x_len != dh_getlen (ie->group))
+ {
+ /* XXX Is this a good notify type? */
+ message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 0, 0);
+ return -1;
+ }
+
+ return ipsec_g_x (msg, 1, kep->p + ISAKMP_KE_DATA_OFF);
+}
+
+/*
+ * Get a SPI for PROTO and the transport MSG passed over. Store the
+ * size where SZ points. NB! A zero return is OK if *SZ is zero.
+ */
+static u_int8_t *
+ipsec_get_spi (size_t *sz, u_int8_t proto, struct message *msg)
+{
+ struct sockaddr *dst;
+ int dstlen;
+ struct transport *transport = msg->transport;
+
+ if (msg->exchange->phase == 1)
+ {
+ *sz = 0;
+ return 0;
+ }
+ else
+ {
+ /* We are the destination in the SA we want a SPI for. */
+ transport->vtbl->get_src (transport, &dst, &dstlen);
+ return sysdep_ipsec_get_spi (sz, proto, dst, dstlen);
+ }
+}
+
+int
+ipsec_esp_enckeylength (struct proto *proto)
+{
+ struct ipsec_proto *iproto = proto->data;
+
+ /* Compute the keylength to use. */
+ switch (proto->id)
+ {
+ case IPSEC_ESP_DES:
+ case IPSEC_ESP_DES_IV32:
+ case IPSEC_ESP_DES_IV64:
+ return 8;
+ case IPSEC_ESP_3DES:
+ return 24;
+ default:
+ return iproto->keylen / 8;
+ }
+}
+
+int
+ipsec_esp_authkeylength (struct proto *proto)
+{
+ struct ipsec_proto *iproto = proto->data;
+
+ switch (iproto->auth)
+ {
+ case IPSEC_AUTH_HMAC_MD5:
+ return 16;
+ case IPSEC_AUTH_HMAC_SHA:
+ return 20;
+ default:
+ return 0;
+ }
+}
+
+int
+ipsec_ah_keylength (struct proto *proto)
+{
+ switch (proto->id)
+ {
+ case IPSEC_AH_MD5:
+ return 16;
+ case IPSEC_AH_SHA:
+ return 20;
+ default:
+ return -1;
+ }
+}
+
+int
+ipsec_keymat_length (struct proto *proto)
+{
+ switch (proto->proto)
+ {
+ case IPSEC_PROTO_IPSEC_ESP:
+ return ipsec_esp_enckeylength (proto) + ipsec_esp_authkeylength (proto);
+ case IPSEC_PROTO_IPSEC_AH:
+ return ipsec_ah_keylength (proto);
+ default:
+ return -1;
+ }
+}