diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2013-11-04 11:33:00 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2013-11-04 15:28:21 +0100 |
commit | af5333e0a02b2295304d4e029b15ee15a4fe2b3a (patch) | |
tree | c5c43680d374f58b487eeeaf18fb7ec6b84ba074 /freebsd/sys/netipsec | |
parent | BUS_SPACE(9): Use simple memory model for ARM (diff) | |
download | rtems-libbsd-af5333e0a02b2295304d4e029b15ee15a4fe2b3a.tar.bz2 |
Update to FreeBSD 8.4
Diffstat (limited to 'freebsd/sys/netipsec')
-rw-r--r-- | freebsd/sys/netipsec/ipsec.h | 16 | ||||
-rw-r--r-- | freebsd/sys/netipsec/ipsec_output.c | 3 | ||||
-rw-r--r-- | freebsd/sys/netipsec/key.c | 73 | ||||
-rw-r--r-- | freebsd/sys/netipsec/key.h | 3 | ||||
-rw-r--r-- | freebsd/sys/netipsec/xform.h | 2 | ||||
-rw-r--r-- | freebsd/sys/netipsec/xform_ah.c | 44 | ||||
-rw-r--r-- | freebsd/sys/netipsec/xform_esp.c | 87 | ||||
-rw-r--r-- | freebsd/sys/netipsec/xform_ipcomp.c | 43 |
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); } |