summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netipsec
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2013-11-04 11:33:00 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2013-11-04 15:28:21 +0100
commitaf5333e0a02b2295304d4e029b15ee15a4fe2b3a (patch)
treec5c43680d374f58b487eeeaf18fb7ec6b84ba074 /freebsd/sys/netipsec
parentBUS_SPACE(9): Use simple memory model for ARM (diff)
downloadrtems-libbsd-af5333e0a02b2295304d4e029b15ee15a4fe2b3a.tar.bz2
Update to FreeBSD 8.4
Diffstat (limited to 'freebsd/sys/netipsec')
-rw-r--r--freebsd/sys/netipsec/ipsec.h16
-rw-r--r--freebsd/sys/netipsec/ipsec_output.c3
-rw-r--r--freebsd/sys/netipsec/key.c73
-rw-r--r--freebsd/sys/netipsec/key.h3
-rw-r--r--freebsd/sys/netipsec/xform.h2
-rw-r--r--freebsd/sys/netipsec/xform_ah.c44
-rw-r--r--freebsd/sys/netipsec/xform_esp.c87
-rw-r--r--freebsd/sys/netipsec/xform_ipcomp.c43
8 files changed, 180 insertions, 91 deletions
diff --git a/freebsd/sys/netipsec/ipsec.h b/freebsd/sys/netipsec/ipsec.h
index d54e7cff..fb987ad2 100644
--- a/freebsd/sys/netipsec/ipsec.h
+++ b/freebsd/sys/netipsec/ipsec.h
@@ -123,7 +123,7 @@ struct ipsecrequest {
struct secasvar *sav; /* place holder of SA for use */
struct secpolicy *sp; /* back pointer to SP */
- struct mtx lock; /* to interlock updates */
+ struct rwlock lock; /* to interlock updates */
};
/*
@@ -132,11 +132,15 @@ struct ipsecrequest {
* hard it is to remove this...
*/
#define IPSECREQUEST_LOCK_INIT(_isr) \
- mtx_init(&(_isr)->lock, "ipsec request", NULL, MTX_DEF | MTX_RECURSE)
-#define IPSECREQUEST_LOCK(_isr) mtx_lock(&(_isr)->lock)
-#define IPSECREQUEST_UNLOCK(_isr) mtx_unlock(&(_isr)->lock)
-#define IPSECREQUEST_LOCK_DESTROY(_isr) mtx_destroy(&(_isr)->lock)
-#define IPSECREQUEST_LOCK_ASSERT(_isr) mtx_assert(&(_isr)->lock, MA_OWNED)
+ rw_init_flags(&(_isr)->lock, "ipsec request", RW_RECURSE)
+#define IPSECREQUEST_LOCK(_isr) rw_rlock(&(_isr)->lock)
+#define IPSECREQUEST_UNLOCK(_isr) rw_runlock(&(_isr)->lock)
+#define IPSECREQUEST_WLOCK(_isr) rw_wlock(&(_isr)->lock)
+#define IPSECREQUEST_WUNLOCK(_isr) rw_wunlock(&(_isr)->lock)
+#define IPSECREQUEST_UPGRADE(_isr) rw_try_upgrade(&(_isr)->lock)
+#define IPSECREQUEST_DOWNGRADE(_isr) rw_downgrade(&(_isr)->lock)
+#define IPSECREQUEST_LOCK_DESTROY(_isr) rw_destroy(&(_isr)->lock)
+#define IPSECREQUEST_LOCK_ASSERT(_isr) rw_assert(&(_isr)->lock, RA_LOCKED)
/* security policy in PCB */
struct inpcbpolicy {
diff --git a/freebsd/sys/netipsec/ipsec_output.c b/freebsd/sys/netipsec/ipsec_output.c
index d477b5f4..9f7051f7 100644
--- a/freebsd/sys/netipsec/ipsec_output.c
+++ b/freebsd/sys/netipsec/ipsec_output.c
@@ -249,7 +249,6 @@ ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
panic("ipsec_process_done");
bad:
m_freem(m);
- KEY_FREESAV(&sav);
return (error);
}
@@ -846,7 +845,7 @@ ipsec6_output_tunnel(struct ipsec_output_state *state, struct secpolicy *sp, int
dst6->sin6_family = AF_INET6;
dst6->sin6_len = sizeof(*dst6);
dst6->sin6_addr = ip6->ip6_dst;
- rtalloc(state->ro);
+ rtalloc_ign_fib(state->ro, 0UL, M_GETFIB(m));
}
if (state->ro->ro_rt == NULL) {
V_ip6stat.ip6s_noroute++;
diff --git a/freebsd/sys/netipsec/key.c b/freebsd/sys/netipsec/key.c
index b6fb97a0..0252730f 100644
--- a/freebsd/sys/netipsec/key.c
+++ b/freebsd/sys/netipsec/key.c
@@ -811,6 +811,7 @@ key_checkrequest(struct ipsecrequest *isr, const struct secasindex *saidx)
{
u_int level;
int error;
+ struct secasvar *sav;
IPSEC_ASSERT(isr != NULL, ("null isr"));
IPSEC_ASSERT(saidx != NULL, ("null saidx"));
@@ -828,45 +829,31 @@ key_checkrequest(struct ipsecrequest *isr, const struct secasindex *saidx)
/* get current level */
level = ipsec_get_reqlevel(isr);
-#if 0
- /*
- * We do allocate new SA only if the state of SA in the holder is
- * SADB_SASTATE_DEAD. The SA for outbound must be the oldest.
- */
- if (isr->sav != NULL) {
- if (isr->sav->sah == NULL)
- panic("%s: sah is null.\n", __func__);
- if (isr->sav == (struct secasvar *)LIST_FIRST(
- &isr->sav->sah->savtree[SADB_SASTATE_DEAD])) {
- KEY_FREESAV(&isr->sav);
- isr->sav = NULL;
- }
- }
-#else
+
/*
- * we free any SA stashed in the IPsec request because a different
+ * We check new SA in the IPsec request because a different
* SA may be involved each time this request is checked, either
* because new SAs are being configured, or this request is
* associated with an unconnected datagram socket, or this request
* is associated with a system default policy.
*
- * The operation may have negative impact to performance. We may
- * want to check cached SA carefully, rather than picking new SA
- * every time.
- */
- if (isr->sav != NULL) {
- KEY_FREESAV(&isr->sav);
- isr->sav = NULL;
- }
-#endif
-
- /*
- * new SA allocation if no SA found.
* key_allocsa_policy should allocate the oldest SA available.
* See key_do_allocsa_policy(), and draft-jenkins-ipsec-rekeying-03.txt.
*/
- if (isr->sav == NULL)
- isr->sav = key_allocsa_policy(saidx);
+ sav = key_allocsa_policy(saidx);
+ if (sav != isr->sav) {
+ /* SA need to be updated. */
+ if (!IPSECREQUEST_UPGRADE(isr)) {
+ /* Kick everyone off. */
+ IPSECREQUEST_UNLOCK(isr);
+ IPSECREQUEST_WLOCK(isr);
+ }
+ if (isr->sav != NULL)
+ KEY_FREESAV(&isr->sav);
+ isr->sav = sav;
+ IPSECREQUEST_DOWNGRADE(isr);
+ } else if (sav != NULL)
+ KEY_FREESAV(&sav);
/* When there is SA. */
if (isr->sav != NULL) {
@@ -1241,6 +1228,16 @@ key_freesp_so(struct secpolicy **sp)
KEY_FREESP(sp);
}
+void
+key_addrefsa(struct secasvar *sav, const char* where, int tag)
+{
+
+ IPSEC_ASSERT(sav != NULL, ("null sav"));
+ IPSEC_ASSERT(sav->refcnt > 0, ("refcount must exist"));
+
+ sa_addref(sav);
+}
+
/*
* Must be called after calling key_allocsa().
* This function is called by key_freesp() to free some SA allocated
@@ -1768,6 +1765,7 @@ key_gather_mbuf(m, mhp, ndeep, nitem, va_alist)
fail:
m_freem(result);
+ va_end(ap);
return NULL;
}
@@ -2297,6 +2295,7 @@ key_spdget(so, m, mhp)
}
n = key_setdumpsp(sp, SADB_X_SPDGET, 0, mhp->msg->sadb_msg_pid);
+ KEY_FREESP(&sp);
if (n != NULL) {
m_freem(m);
return key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
@@ -2870,9 +2869,10 @@ key_newsav(m, mhp, sah, errp, where, tag)
sa_initref(newsav);
newsav->state = SADB_SASTATE_LARVAL;
- /* XXX locking??? */
+ SAHTREE_LOCK();
LIST_INSERT_TAIL(&sah->savtree[SADB_SASTATE_LARVAL], newsav,
secasvar, chain);
+ SAHTREE_UNLOCK();
done:
KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
printf("DP %s from %s:%u return SP:%p\n", __func__,
@@ -5716,8 +5716,8 @@ key_delete(so, m, mhp)
}
key_sa_chgstate(sav, SADB_SASTATE_DEAD);
- SAHTREE_UNLOCK();
KEY_FREESAV(&sav);
+ SAHTREE_UNLOCK();
{
struct mbuf *n;
@@ -6105,6 +6105,9 @@ key_getsizes_ah(
case SADB_X_AALG_MD5: *min = *max = 16; break;
case SADB_X_AALG_SHA: *min = *max = 20; break;
case SADB_X_AALG_NULL: *min = 1; *max = 256; break;
+ case SADB_X_AALG_SHA2_256: *min = *max = 32; break;
+ case SADB_X_AALG_SHA2_384: *min = *max = 48; break;
+ case SADB_X_AALG_SHA2_512: *min = *max = 64; break;
default:
DPRINTF(("%s: unknown AH algorithm %u\n",
__func__, alg));
@@ -6130,7 +6133,11 @@ key_getcomb_ah()
for (i = 1; i <= SADB_AALG_MAX; i++) {
#if 1
/* we prefer HMAC algorithms, not old algorithms */
- if (i != SADB_AALG_SHA1HMAC && i != SADB_AALG_MD5HMAC)
+ if (i != SADB_AALG_SHA1HMAC &&
+ i != SADB_AALG_MD5HMAC &&
+ i != SADB_X_AALG_SHA2_256 &&
+ i != SADB_X_AALG_SHA2_384 &&
+ i != SADB_X_AALG_SHA2_512)
continue;
#endif
algo = ah_algorithm_lookup(i);
diff --git a/freebsd/sys/netipsec/key.h b/freebsd/sys/netipsec/key.h
index e85acd1e..f246dbcf 100644
--- a/freebsd/sys/netipsec/key.h
+++ b/freebsd/sys/netipsec/key.h
@@ -76,10 +76,13 @@ extern void _key_freesp(struct secpolicy **, const char*, int);
extern struct secasvar *key_allocsa(union sockaddr_union *, u_int, u_int32_t,
const char*, int);
+extern void key_addrefsa(struct secasvar *, const char*, int);
extern void key_freesav(struct secasvar **, const char*, int);
#define KEY_ALLOCSA(dst, proto, spi) \
key_allocsa(dst, proto, spi, __FILE__, __LINE__)
+#define KEY_ADDREFSA(sav) \
+ key_addrefsa(sav, __FILE__, __LINE__)
#define KEY_FREESAV(psav) \
key_freesav(psav, __FILE__, __LINE__)
diff --git a/freebsd/sys/netipsec/xform.h b/freebsd/sys/netipsec/xform.h
index fc259c91..77b93af3 100644
--- a/freebsd/sys/netipsec/xform.h
+++ b/freebsd/sys/netipsec/xform.h
@@ -46,6 +46,7 @@
#include <opencrypto/xform.h>
#define AH_HMAC_HASHLEN 12 /* 96 bits of authenticator */
+#define AH_HMAC_MAXHASHLEN (SHA2_512_HASH_LEN/2) /* Keep this updated */
#define AH_HMAC_INITIAL_RPL 1 /* replay counter initial value */
/*
@@ -74,6 +75,7 @@ struct tdb_crypto {
int tc_protoff; /* current protocol offset */
int tc_skip; /* data offset */
caddr_t tc_ptr; /* associated crypto data */
+ struct secasvar *tc_sav; /* related SA */
};
struct secasvar;
diff --git a/freebsd/sys/netipsec/xform_ah.c b/freebsd/sys/netipsec/xform_ah.c
index f5c0929b..98d319ce 100644
--- a/freebsd/sys/netipsec/xform_ah.c
+++ b/freebsd/sys/netipsec/xform_ah.c
@@ -87,8 +87,7 @@
* to use a fixed 16-byte authenticator. The new algorithm use 12-byte
* authenticator.
*/
-#define AUTHSIZE(sav) \
- ((sav->flags & SADB_X_EXT_OLD) ? 16 : AH_HMAC_HASHLEN)
+#define AUTHSIZE(sav) ah_authsize(sav)
VNET_DEFINE(int, ah_enable) = 1; /* control flow of packets with AH */
VNET_DEFINE(int, ah_cleartos) = 1; /* clear ip_tos when doing AH calc */
@@ -107,6 +106,27 @@ static unsigned char ipseczeroes[256]; /* larger than an ip6 extension hdr */
static int ah_input_cb(struct cryptop*);
static int ah_output_cb(struct cryptop*);
+static int
+ah_authsize(struct secasvar *sav)
+{
+
+ IPSEC_ASSERT(sav != NULL, ("%s: sav == NULL", __func__));
+
+ if (sav->flags & SADB_X_EXT_OLD)
+ return 16;
+
+ switch (sav->alg_auth) {
+ case SADB_X_AALG_SHA2_256:
+ return 16;
+ case SADB_X_AALG_SHA2_384:
+ return 24;
+ case SADB_X_AALG_SHA2_512:
+ return 32;
+ default:
+ return AH_HMAC_HASHLEN;
+ }
+ /* NOTREACHED */
+}
/*
* NB: this is public for use by the PF_KEY support.
*/
@@ -697,6 +717,8 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
tc->tc_protoff = protoff;
tc->tc_skip = skip;
tc->tc_ptr = (caddr_t) mtag; /* Save the mtag we've identified. */
+ KEY_ADDREFSA(sav);
+ tc->tc_sav = sav;
if (mtag == NULL)
return crypto_dispatch(crp);
@@ -746,13 +768,8 @@ ah_input_cb(struct cryptop *crp)
mtag = (struct m_tag *) tc->tc_ptr;
m = (struct mbuf *) crp->crp_buf;
- sav = KEY_ALLOCSA(&tc->tc_dst, tc->tc_proto, tc->tc_spi);
- if (sav == NULL) {
- V_ahstat.ahs_notdb++;
- DPRINTF(("%s: SA expired while in crypto\n", __func__));
- error = ENOBUFS; /*XXX*/
- goto bad;
- }
+ sav = tc->tc_sav;
+ IPSEC_ASSERT(sav != NULL, ("null SA!"));
saidx = &sav->sah->saidx;
IPSEC_ASSERT(saidx->dst.sa.sa_family == AF_INET ||
@@ -1092,6 +1109,8 @@ ah_output(
/* These are passed as-is to the callback. */
tc->tc_isr = isr;
+ KEY_ADDREFSA(sav);
+ tc->tc_sav = sav;
tc->tc_spi = sav->spi;
tc->tc_dst = sav->sah->saidx.dst;
tc->tc_proto = sav->sah->saidx.proto;
@@ -1128,14 +1147,14 @@ ah_output_cb(struct cryptop *crp)
isr = tc->tc_isr;
IPSECREQUEST_LOCK(isr);
- sav = KEY_ALLOCSA(&tc->tc_dst, tc->tc_proto, tc->tc_spi);
- if (sav == NULL) {
+ sav = tc->tc_sav;
+ /* With the isr lock released SA pointer can be updated. */
+ if (sav != isr->sav) {
V_ahstat.ahs_notdb++;
DPRINTF(("%s: SA expired while in crypto\n", __func__));
error = ENOBUFS; /*XXX*/
goto bad;
}
- IPSEC_ASSERT(isr->sav == sav, ("SA changed\n"));
/* Check for crypto errors. */
if (crp->crp_etype) {
@@ -1143,7 +1162,6 @@ ah_output_cb(struct cryptop *crp)
sav->tdb_cryptoid = crp->crp_sid;
if (crp->crp_etype == EAGAIN) {
- KEY_FREESAV(&sav);
IPSECREQUEST_UNLOCK(isr);
error = crypto_dispatch(crp);
return error;
diff --git a/freebsd/sys/netipsec/xform_esp.c b/freebsd/sys/netipsec/xform_esp.c
index 04d7bd51..4e52cde1 100644
--- a/freebsd/sys/netipsec/xform_esp.c
+++ b/freebsd/sys/netipsec/xform_esp.c
@@ -299,7 +299,19 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
else
hlen = sizeof (struct newesp) + sav->ivlen;
/* Authenticator hash size */
- alen = esph ? AH_HMAC_HASHLEN : 0;
+ if (esph != NULL) {
+ switch (esph->type) {
+ case CRYPTO_SHA2_256_HMAC:
+ case CRYPTO_SHA2_384_HMAC:
+ case CRYPTO_SHA2_512_HMAC:
+ alen = esph->hashsize/2;
+ break;
+ default:
+ alen = AH_HMAC_HASHLEN;
+ break;
+ }
+ }else
+ alen = 0;
/*
* Verify payload length is multiple of encryption algorithm
@@ -413,6 +425,8 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
tc->tc_proto = sav->sah->saidx.proto;
tc->tc_protoff = protoff;
tc->tc_skip = skip;
+ KEY_ADDREFSA(sav);
+ tc->tc_sav = sav;
/* Decryption descriptor */
if (espx) {
@@ -452,8 +466,8 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
static int
esp_input_cb(struct cryptop *crp)
{
- u_int8_t lastthree[3], aalg[AH_HMAC_HASHLEN];
- int hlen, skip, protoff, error;
+ u_int8_t lastthree[3], aalg[AH_HMAC_MAXHASHLEN];
+ int hlen, skip, protoff, error, alen;
struct mbuf *m;
struct cryptodesc *crd;
struct auth_hash *esph;
@@ -474,15 +488,8 @@ esp_input_cb(struct cryptop *crp)
mtag = (struct m_tag *) tc->tc_ptr;
m = (struct mbuf *) crp->crp_buf;
- sav = KEY_ALLOCSA(&tc->tc_dst, tc->tc_proto, tc->tc_spi);
- if (sav == NULL) {
- V_espstat.esps_notdb++;
- DPRINTF(("%s: SA gone during crypto (SA %s/%08lx proto %u)\n",
- __func__, ipsec_address(&tc->tc_dst),
- (u_long) ntohl(tc->tc_spi), tc->tc_proto));
- error = ENOBUFS; /*XXX*/
- goto bad;
- }
+ sav = tc->tc_sav;
+ IPSEC_ASSERT(sav != NULL, ("null SA!"));
saidx = &sav->sah->saidx;
IPSEC_ASSERT(saidx->dst.sa.sa_family == AF_INET ||
@@ -499,7 +506,6 @@ esp_input_cb(struct cryptop *crp)
sav->tdb_cryptoid = crp->crp_sid;
if (crp->crp_etype == EAGAIN) {
- KEY_FREESAV(&sav);
error = crypto_dispatch(crp);
return error;
}
@@ -521,6 +527,16 @@ esp_input_cb(struct cryptop *crp)
/* If authentication was performed, check now. */
if (esph != NULL) {
+ switch (esph->type) {
+ case CRYPTO_SHA2_256_HMAC:
+ case CRYPTO_SHA2_384_HMAC:
+ case CRYPTO_SHA2_512_HMAC:
+ alen = esph->hashsize/2;
+ break;
+ default:
+ alen = AH_HMAC_HASHLEN;
+ break;
+ }
/*
* If we have a tag, it means an IPsec-aware NIC did
* the verification for us. Otherwise we need to
@@ -529,13 +545,13 @@ esp_input_cb(struct cryptop *crp)
V_ahstat.ahs_hist[sav->alg_auth]++;
if (mtag == NULL) {
/* Copy the authenticator from the packet */
- m_copydata(m, m->m_pkthdr.len - AH_HMAC_HASHLEN,
- AH_HMAC_HASHLEN, aalg);
+ m_copydata(m, m->m_pkthdr.len - alen,
+ alen, aalg);
ptr = (caddr_t) (tc + 1);
/* Verify authenticator */
- if (bcmp(ptr, aalg, AH_HMAC_HASHLEN) != 0) {
+ if (bcmp(ptr, aalg, alen) != 0) {
DPRINTF(("%s: "
"authentication hash mismatch for packet in SA %s/%08lx\n",
__func__,
@@ -548,7 +564,7 @@ esp_input_cb(struct cryptop *crp)
}
/* Remove trailing authenticator */
- m_adj(m, -AH_HMAC_HASHLEN);
+ m_adj(m, -alen);
}
/* Release the crypto descriptors */
@@ -692,7 +708,16 @@ esp_output(
plen = rlen + padding; /* Padded payload length. */
if (esph)
+ switch (esph->type) {
+ case CRYPTO_SHA2_256_HMAC:
+ case CRYPTO_SHA2_384_HMAC:
+ case CRYPTO_SHA2_512_HMAC:
+ alen = esph->hashsize/2;
+ break;
+ default:
alen = AH_HMAC_HASHLEN;
+ break;
+ }
else
alen = 0;
@@ -848,6 +873,8 @@ esp_output(
/* Callback parameters */
tc->tc_isr = isr;
+ KEY_ADDREFSA(sav);
+ tc->tc_sav = sav;
tc->tc_spi = sav->spi;
tc->tc_dst = saidx->dst;
tc->tc_proto = saidx->proto;
@@ -897,8 +924,9 @@ esp_output_cb(struct cryptop *crp)
isr = tc->tc_isr;
IPSECREQUEST_LOCK(isr);
- sav = KEY_ALLOCSA(&tc->tc_dst, tc->tc_proto, tc->tc_spi);
- if (sav == NULL) {
+ sav = tc->tc_sav;
+ /* With the isr lock released SA pointer can be updated. */
+ if (sav != isr->sav) {
V_espstat.esps_notdb++;
DPRINTF(("%s: SA gone during crypto (SA %s/%08lx proto %u)\n",
__func__, ipsec_address(&tc->tc_dst),
@@ -906,8 +934,6 @@ esp_output_cb(struct cryptop *crp)
error = ENOBUFS; /*XXX*/
goto bad;
}
- IPSEC_ASSERT(isr->sav == sav,
- ("SA changed was %p now %p\n", isr->sav, sav));
/* Check for crypto errors. */
if (crp->crp_etype) {
@@ -916,7 +942,6 @@ esp_output_cb(struct cryptop *crp)
sav->tdb_cryptoid = crp->crp_sid;
if (crp->crp_etype == EAGAIN) {
- KEY_FREESAV(&sav);
IPSECREQUEST_UNLOCK(isr);
error = crypto_dispatch(crp);
return error;
@@ -946,7 +971,7 @@ esp_output_cb(struct cryptop *crp)
#ifdef REGRESSION
/* Emulate man-in-the-middle attack when ipsec_integrity is TRUE. */
if (V_ipsec_integrity) {
- static unsigned char ipseczeroes[AH_HMAC_HASHLEN];
+ static unsigned char ipseczeroes[AH_HMAC_MAXHASHLEN];
struct auth_hash *esph;
/*
@@ -955,8 +980,20 @@ esp_output_cb(struct cryptop *crp)
*/
esph = sav->tdb_authalgxform;
if (esph != NULL) {
- m_copyback(m, m->m_pkthdr.len - AH_HMAC_HASHLEN,
- AH_HMAC_HASHLEN, ipseczeroes);
+ int alen;
+
+ switch (esph->type) {
+ case CRYPTO_SHA2_256_HMAC:
+ case CRYPTO_SHA2_384_HMAC:
+ case CRYPTO_SHA2_512_HMAC:
+ alen = esph->hashsize/2;
+ break;
+ default:
+ alen = AH_HMAC_HASHLEN;
+ break;
+ }
+ m_copyback(m, m->m_pkthdr.len - alen,
+ alen, ipseczeroes);
}
}
#endif
diff --git a/freebsd/sys/netipsec/xform_ipcomp.c b/freebsd/sys/netipsec/xform_ipcomp.c
index 2c0f961f..c4048cc9 100644
--- a/freebsd/sys/netipsec/xform_ipcomp.c
+++ b/freebsd/sys/netipsec/xform_ipcomp.c
@@ -39,6 +39,7 @@
#include <sys/mbuf.h>
#include <rtems/bsd/sys/lock.h>
#include <sys/mutex.h>
+#include <sys/rwlock.h>
#include <sys/socket.h>
#include <sys/kernel.h>
#include <sys/protosw.h>
@@ -143,8 +144,29 @@ ipcomp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
struct tdb_crypto *tc;
struct cryptodesc *crdc;
struct cryptop *crp;
+ struct ipcomp *ipcomp;
+ caddr_t addr;
int hlen = IPCOMP_HLENGTH;
+ /*
+ * Check that the next header of the IPComp is not IPComp again, before
+ * doing any real work. Given it is not possible to do double
+ * compression it means someone is playing tricks on us.
+ */
+ if (m->m_len < skip + hlen && (m = m_pullup(m, skip + hlen)) == NULL) {
+ V_ipcompstat.ipcomps_hdrops++; /*XXX*/
+ DPRINTF(("%s: m_pullup failed\n", __func__));
+ return (ENOBUFS);
+ }
+ addr = (caddr_t) mtod(m, struct ip *) + skip;
+ ipcomp = (struct ipcomp *)addr;
+ if (ipcomp->comp_nxt == IPPROTO_IPCOMP) {
+ m_freem(m);
+ V_ipcompstat.ipcomps_pdrops++; /* XXX have our own stats? */
+ DPRINTF(("%s: recursive compression detected\n", __func__));
+ return (EINVAL);
+ }
+
/* Get crypto descriptors */
crp = crypto_getreq(1);
if (crp == NULL) {
@@ -187,6 +209,8 @@ ipcomp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
tc->tc_proto = sav->sah->saidx.proto;
tc->tc_protoff = protoff;
tc->tc_skip = skip;
+ KEY_ADDREFSA(sav);
+ tc->tc_sav = sav;
return crypto_dispatch(crp);
}
@@ -230,13 +254,8 @@ ipcomp_input_cb(struct cryptop *crp)
mtag = (struct mtag *) tc->tc_ptr;
m = (struct mbuf *) crp->crp_buf;
- sav = KEY_ALLOCSA(&tc->tc_dst, tc->tc_proto, tc->tc_spi);
- if (sav == NULL) {
- V_ipcompstat.ipcomps_notdb++;
- DPRINTF(("%s: SA expired while in crypto\n", __func__));
- error = ENOBUFS; /*XXX*/
- goto bad;
- }
+ sav = tc->tc_sav;
+ IPSEC_ASSERT(sav != NULL, ("null SA!"));
saidx = &sav->sah->saidx;
IPSEC_ASSERT(saidx->dst.sa.sa_family == AF_INET ||
@@ -250,7 +269,6 @@ ipcomp_input_cb(struct cryptop *crp)
sav->tdb_cryptoid = crp->crp_sid;
if (crp->crp_etype == EAGAIN) {
- KEY_FREESAV(&sav);
return crypto_dispatch(crp);
}
V_ipcompstat.ipcomps_noxform++;
@@ -433,6 +451,8 @@ ipcomp_output(
}
tc->tc_isr = isr;
+ KEY_ADDREFSA(sav);
+ tc->tc_sav = sav;
tc->tc_spi = sav->spi;
tc->tc_dst = sav->sah->saidx.dst;
tc->tc_proto = sav->sah->saidx.proto;
@@ -473,14 +493,14 @@ ipcomp_output_cb(struct cryptop *crp)
isr = tc->tc_isr;
IPSECREQUEST_LOCK(isr);
- sav = KEY_ALLOCSA(&tc->tc_dst, tc->tc_proto, tc->tc_spi);
- if (sav == NULL) {
+ sav = tc->tc_sav;
+ /* With the isr lock released SA pointer can be updated. */
+ if (sav != isr->sav) {
V_ipcompstat.ipcomps_notdb++;
DPRINTF(("%s: SA expired while in crypto\n", __func__));
error = ENOBUFS; /*XXX*/
goto bad;
}
- IPSEC_ASSERT(isr->sav == sav, ("SA changed\n"));
/* Check for crypto errors */
if (crp->crp_etype) {
@@ -489,7 +509,6 @@ ipcomp_output_cb(struct cryptop *crp)
sav->tdb_cryptoid = crp->crp_sid;
if (crp->crp_etype == EAGAIN) {
- KEY_FREESAV(&sav);
IPSECREQUEST_UNLOCK(isr);
return crypto_dispatch(crp);
}