Apply by doing: cd /usr/src patch -p0 < 014_isakmpd.patch Then rebuild and install isakmpd: cd sbin/isakmpd make clean make depend make make install Index: sbin/isakmpd/exchange.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/exchange.c,v retrieving revision 1.78 retrieving revision 1.78.2.1 diff -u -p -r1.78 -r1.78.2.1 --- sbin/isakmpd/exchange.c 6 Mar 2003 13:32:42 -0000 1.78 +++ sbin/isakmpd/exchange.c 16 Jan 2004 00:00:21 -0000 1.78.2.1 @@ -225,8 +225,10 @@ exchange_validate (struct message *msg) && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]) && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SIG])) || (*pc == EXCHANGE_SCRIPT_INFO - && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_NOTIFY]) - && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_DELETE]))) + && ((!TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_NOTIFY]) + && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_DELETE])) + || (TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_DELETE]) + && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]))))) { /* Missing payload. */ LOG_DBG ((LOG_MESSAGE, 70, Index: sbin/isakmpd/ipsec.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/ipsec.c,v retrieving revision 1.72.2.1 retrieving revision 1.72.2.2 diff -u -p -r1.72.2.1 -r1.72.2.2 --- sbin/isakmpd/ipsec.c 13 Jun 2003 03:50:53 -0000 1.72.2.1 +++ sbin/isakmpd/ipsec.c 16 Jan 2004 00:00:21 -0000 1.72.2.2 @@ -1017,43 +1017,6 @@ ipsec_delete_spi_list (struct sockaddr * } } -/* - * deal with a NOTIFY of INVALID_SPI - */ -static void -ipsec_invalid_spi (struct message *msg, struct payload *p) -{ - struct sockaddr *dst; - int invspisz, off; - u_int32_t spi; - u_int16_t totsiz; - u_int8_t spisz; - - /* - * get the invalid spi out of the variable sized notification data - * field, which is after the variable sized SPI field [which specifies - * the receiving entity's phase-1 SPI, not the invalid spi] - */ - totsiz = GET_ISAKMP_GEN_LENGTH (p->p); - spisz = GET_ISAKMP_NOTIFY_SPI_SZ (p->p); - off = ISAKMP_NOTIFY_SPI_OFF + spisz; - invspisz = totsiz - off; - - if (invspisz != sizeof spi) - { - LOG_DBG ((LOG_SA, 40, - "ipsec_invalid_spi: SPI size %d in INVALID_SPI " - "payload unsupported", spisz)); - return; - } - memcpy (&spi, p->p + off, sizeof spi); - - msg->transport->vtbl->get_dst (msg->transport, &dst); - - /* delete matching SPI's from this peer */ - ipsec_delete_spi_list (dst, 0, (u_int8_t *)&spi, 1, "INVALID_SPI"); -} - static int ipsec_responder (struct message *msg) { @@ -1103,9 +1066,6 @@ ipsec_responder (struct message *msg) "ipsec_responder: got NOTIFY of type %s", constant_lookup (isakmp_notify_cst, type))); - if (type == ISAKMP_NOTIFY_INVALID_SPI) - ipsec_invalid_spi (msg, p); - p->flags |= PL_MARK; } @@ -1658,6 +1618,31 @@ ipsec_handle_leftover_payload (struct me switch (GET_ISAKMP_NOTIFY_MSG_TYPE (payload->p)) { case IPSEC_NOTIFY_INITIAL_CONTACT: + /* + * Permit INITIAL-CONTACT if + * - this is not an AGGRESSIVE mode exchange + * - it is protected by an ISAKMP SA + * + * XXX Instead of the first condition above, we could permit this + * XXX only for phase 2. In the last packet of main-mode, this + * XXX payload, while encrypted, is not part of the hash digest. + * XXX As we currently send our own INITIAL-CONTACTs at this point, + * XXX this too would need to be changed. + */ + if (msg->exchange->type == ISAKMP_EXCH_AGGRESSIVE) + { + log_print ("ipsec_handle_leftover_payload: got INITIAL-CONTACT " + "in AGGRESSIVE mode"); + return -1; + } + + if ((msg->exchange->flags & EXCHANGE_FLAG_ENCRYPT) == 0) + { + log_print ("ipsec_handle_leftover_payload: got INITIAL-CONTACT " + "without ISAKMP SA"); + return -1; + } + /* * Find out who is sending this and then delete every SA that is * ready. Exchanges will timeout themselves and then the Index: sbin/isakmpd/message.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/message.c,v retrieving revision 1.57 retrieving revision 1.57.4.1 diff -u -p -r1.57 -r1.57.4.1 --- sbin/isakmpd/message.c 11 Sep 2002 09:50:44 -0000 1.57 +++ sbin/isakmpd/message.c 16 Jan 2004 00:00:21 -0000 1.57.4.1 @@ -52,10 +52,13 @@ #include "doi.h" #include "exchange.h" #include "field.h" +#include "hash.h" +#include "ipsec.h" #include "ipsec_num.h" #include "isakmp.h" #include "log.h" #include "message.h" +#include "prf.h" #include "sa.h" #include "timer.h" #include "transport.h" @@ -444,6 +447,12 @@ message_validate_delete (struct message { u_int8_t proto = GET_ISAKMP_DELETE_PROTO (p->p); struct doi *doi; + struct sa *sa, *isakmp_sa; + struct sockaddr *dst, *dst_isa; + u_int32_t nspis = GET_ISAKMP_DELETE_NSPIS (p->p); + u_int8_t *spis = (u_int8_t *)p->p + ISAKMP_DELETE_SPI_OFF; + int i; + char *addr; doi = doi_lookup (GET_ISAKMP_DELETE_DOI (p->p)); if (!doi) @@ -477,16 +486,141 @@ message_validate_delete (struct message } /* Validate the SPIs. */ + for (i = 0; i < nspis; i++) + { + /* Get ISAKMP SA protecting this message. */ + isakmp_sa = msg->isakmp_sa; + if (!isakmp_sa) + { + /* XXX should not happen? */ + log_print ("message_validate_delete: invalid spi " + "(no valid ISAKMP SA found)"); + message_free (msg); + return -1; + } + isakmp_sa->transport->vtbl->get_dst (isakmp_sa->transport, &dst_isa); + + /* Get SA to be deleted. */ + msg->transport->vtbl->get_dst (msg->transport, &dst); + if (proto == ISAKMP_PROTO_ISAKMP) + sa = sa_lookup_isakmp_sa (dst, spis + i * ISAKMP_HDR_COOKIES_LEN); + else + sa = ipsec_sa_lookup (dst, ((u_int32_t *)spis)[i], proto); + if (!sa) + { + log_print ("message_validate_delete: invalid spi " + "(no valid SA found)"); + message_free (msg); + return -1; + } + sa->transport->vtbl->get_dst (sa->transport, &dst); + + /* Destination addresses must match. */ + if (dst->sa_family != dst_isa->sa_family || + memcmp (sockaddr_addrdata (dst_isa), sockaddr_addrdata (dst), + sockaddr_addrlen (dst))) + { + sockaddr2text (dst_isa, &addr, 0); + + log_print ("message_validate_delete: invalid spi " + "(illegal delete request from %s)", addr); + free (addr); + message_free (msg); + return -1; + } + } return 0; } /* - * Validate the hash payload P in message MSG. */ + * Validate the hash payload P in message MSG. + * XXX Currently hash payloads are processed by the particular exchanges, + * except INFORMATIONAL. This should be actually done here. + */ static int message_validate_hash (struct message *msg, struct payload *p) { - /* XXX Not implemented yet. */ + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa; + struct hash *hash; + struct payload *hashp = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]); + struct prf *prf; + u_int8_t *comp_hash, *rest; + u_int8_t message_id[ISAKMP_HDR_MESSAGE_ID_LEN]; + size_t rest_len; + + if (msg->exchange) /* active exchange validates hash payload. */ + return 0; + + if (isakmp_sa == NULL) + { + log_print ("message_validate_hash: invalid hash information"); + return -1; + } + + isa = isakmp_sa->data; + hash = hash_get (isa->hash); + + if (hash == NULL) + { + log_print ("message_validate_hash: invalid hash information"); + return -1; + } + + /* If no SKEYID_a, we can not do anything (should not happen). */ + if (!isa->skeyid_a) + { + log_print ("message_validate_hash: invalid hash information"); + return -1; + } + + /* Allocate the prf and start calculating our HASH(1). */ + LOG_DBG_BUF ((LOG_MISC, 90, "message_validate_hash: SKEYID_a", isa->skeyid_a, + isa->skeyid_len)); + prf = prf_alloc (isa->prf_type, hash->type, isa->skeyid_a, isa->skeyid_len); + if (!prf) + return -1; + + comp_hash = (u_int8_t *)malloc (hash->hashsize); + if (!comp_hash) + { + log_error ("message_validate_hash: malloc (%lu) failed", + (unsigned long)hash->hashsize); + prf_free (prf); + return -1; + } + + /* This is not an active exchange. */ + GET_ISAKMP_HDR_MESSAGE_ID (msg->iov[0].iov_base, message_id); + + prf->Init (prf->prfctx); + LOG_DBG_BUF ((LOG_MISC, 90, "message_validate_hash: message_id", + message_id, ISAKMP_HDR_MESSAGE_ID_LEN)); + prf->Update (prf->prfctx, message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + rest = hashp->p + GET_ISAKMP_GEN_LENGTH (hashp->p); + rest_len = (GET_ISAKMP_HDR_LENGTH (msg->iov[0].iov_base) + - (rest - (u_int8_t*)msg->iov[0].iov_base)); + LOG_DBG_BUF ((LOG_MISC, 90, "message_validate_hash: payloads after HASH(1)", + rest, rest_len)); + prf->Update (prf->prfctx, rest, rest_len); + prf->Final (comp_hash, prf->prfctx); + prf_free (prf); + + if (memcmp (hashp->p + ISAKMP_HASH_DATA_OFF, comp_hash, hash->hashsize)) + { + log_print ("message_validate_hash: invalid hash value for %s payload", + TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_DELETE]) + ? "DELETE" : "NOTIFY"); + 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; + return 0; } @@ -1221,6 +1355,16 @@ message_recv (struct message *msg) if ((msg->exchange->flags & EXCHANGE_FLAG_COMMITTED) == 0 && (flags & ISAKMP_FLAGS_COMMIT)) msg->exchange->flags |= EXCHANGE_FLAG_HE_COMMITTED; + + /* Require encryption as soon as we have the keystate for it. */ + if ((flags & ISAKMP_FLAGS_ENC) == 0 && + (msg->exchange->phase == 2 || msg->exchange->keystate)) + { + log_print ("message_recv: cleartext phase %d message", + msg->exchange->phase); + message_drop (msg, ISAKMP_NOTIFY_INVALID_FLAGS, 0, 1, 1); + return -1; + } /* OK let the exchange logic do the rest. */ exchange_run (msg);