summaryrefslogtreecommitdiffstats
path: root/sys/netipsec/xform_esp.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netipsec/xform_esp.c')
-rw-r--r--sys/netipsec/xform_esp.c323
1 files changed, 130 insertions, 193 deletions
diff --git a/sys/netipsec/xform_esp.c b/sys/netipsec/xform_esp.c
index c8b5f16..3c5ca6b 100644
--- a/sys/netipsec/xform_esp.c
+++ b/sys/netipsec/xform_esp.c
@@ -46,7 +46,7 @@
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/random.h>
-#include <sys/rwlock.h>
+#include <sys/mutex.h>
#include <sys/sysctl.h>
#include <sys/mutex.h>
#include <machine/atomic.h>
@@ -97,40 +97,6 @@ SYSCTL_VNET_PCPUSTAT(_net_inet_esp, IPSECCTL_STATS, stats,
static int esp_input_cb(struct cryptop *op);
static int esp_output_cb(struct cryptop *crp);
-/*
- * NB: this is public for use by the PF_KEY support.
- * NB: if you add support here; be sure to add code to esp_attach below!
- */
-struct enc_xform *
-esp_algorithm_lookup(int alg)
-{
- if (alg >= ESP_ALG_MAX)
- return NULL;
- switch (alg) {
- case SADB_EALG_DESCBC:
- return &enc_xform_des;
- case SADB_EALG_3DESCBC:
- return &enc_xform_3des;
- case SADB_X_EALG_AES:
- return &enc_xform_rijndael128;
- case SADB_X_EALG_BLOWFISHCBC:
- return &enc_xform_blf;
- case SADB_X_EALG_CAST128CBC:
- return &enc_xform_cast5;
- case SADB_EALG_NULL:
- return &enc_xform_null;
- case SADB_X_EALG_CAMELLIACBC:
- return &enc_xform_camellia;
- case SADB_X_EALG_AESCTR:
- return &enc_xform_aes_icm;
- case SADB_X_EALG_AESGCM16:
- return &enc_xform_aes_nist_gcm;
- case SADB_X_EALG_AESGMAC:
- return &enc_xform_aes_nist_gmac;
- }
- return NULL;
-}
-
size_t
esp_hdrsiz(struct secasvar *sav)
{
@@ -168,12 +134,12 @@ esp_hdrsiz(struct secasvar *sav)
static int
esp_init(struct secasvar *sav, struct xformsw *xsp)
{
- struct enc_xform *txform;
+ const struct enc_xform *txform;
struct cryptoini cria, crie;
int keylen;
int error;
- txform = esp_algorithm_lookup(sav->alg_enc);
+ txform = enc_algorithm_lookup(sav->alg_enc);
if (txform == NULL) {
DPRINTF(("%s: unsupported encryption algorithm %d\n",
__func__, sav->alg_enc));
@@ -298,14 +264,15 @@ static int
esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
{
char buf[128];
- struct auth_hash *esph;
- struct enc_xform *espx;
- struct tdb_crypto *tc;
- uint8_t *ivp;
- int plen, alen, hlen;
- struct newesp *esp;
+ const struct auth_hash *esph;
+ const struct enc_xform *espx;
+ struct xform_data *xd;
struct cryptodesc *crde;
struct cryptop *crp;
+ struct newesp *esp;
+ uint8_t *ivp;
+ uint64_t cryptoid;
+ int plen, alen, hlen;
IPSEC_ASSERT(sav != NULL, ("null SA"));
IPSEC_ASSERT(sav->tdb_encalgxform != NULL, ("null encoding xform"));
@@ -354,14 +321,19 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
/*
* Check sequence number.
*/
- if (esph != NULL && sav->replay != NULL &&
- !ipsec_chkreplay(ntohl(esp->esp_seq), sav)) {
- DPRINTF(("%s: packet replay check for %s\n", __func__,
- ipsec_logsastr(sav, buf, sizeof(buf)))); /*XXX*/
- ESPSTAT_INC(esps_replay);
- m_freem(m);
- return ENOBUFS; /*XXX*/
+ SECASVAR_LOCK(sav);
+ if (esph != NULL && sav->replay != NULL && sav->replay->wsize != 0) {
+ if (ipsec_chkreplay(ntohl(esp->esp_seq), sav) == 0) {
+ SECASVAR_UNLOCK(sav);
+ DPRINTF(("%s: packet replay check for %s\n", __func__,
+ ipsec_sa2str(sav, buf, sizeof(buf))));
+ ESPSTAT_INC(esps_replay);
+ m_freem(m);
+ return (EACCES);
+ }
}
+ cryptoid = sav->tdb_cryptoid;
+ SECASVAR_UNLOCK(sav);
/* Update the counters */
ESPSTAT_ADD(esps_ibytes, m->m_pkthdr.len - (skip + hlen + alen));
@@ -377,12 +349,11 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
}
/* Get IPsec-specific opaque pointer */
- tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto) + alen,
- M_XDATA, M_NOWAIT | M_ZERO);
- if (tc == NULL) {
- crypto_freereq(crp);
- DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__));
+ xd = malloc(sizeof(*xd) + alen, M_XDATA, M_NOWAIT | M_ZERO);
+ if (xd == NULL) {
+ DPRINTF(("%s: failed to allocate xform_data\n", __func__));
ESPSTAT_INC(esps_crypto);
+ crypto_freereq(crp);
m_freem(m);
return ENOBUFS;
}
@@ -404,7 +375,7 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
/* Copy the authenticator */
m_copydata(m, m->m_pkthdr.len - alen, alen,
- (caddr_t) (tc + 1));
+ (caddr_t) (xd + 1));
/* Chain authentication request */
crde = crda->crd_next;
@@ -417,17 +388,14 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC;
crp->crp_buf = (caddr_t) m;
crp->crp_callback = esp_input_cb;
- crp->crp_sid = sav->tdb_cryptoid;
- crp->crp_opaque = (caddr_t) tc;
+ crp->crp_sid = cryptoid;
+ crp->crp_opaque = (caddr_t) xd;
/* These are passed as-is to the callback */
- tc->tc_spi = sav->spi;
- tc->tc_dst = sav->sah->saidx.dst;
- tc->tc_proto = sav->sah->saidx.proto;
- tc->tc_protoff = protoff;
- tc->tc_skip = skip;
- KEY_ADDREFSA(sav);
- tc->tc_sav = sav;
+ xd->sav = sav;
+ xd->protoff = protoff;
+ xd->skip = skip;
+ xd->cryptoid = cryptoid;
/* Decryption descriptor */
IPSEC_ASSERT(crde != NULL, ("null esp crypto descriptor"));
@@ -467,45 +435,39 @@ esp_input_cb(struct cryptop *crp)
{
char buf[128];
u_int8_t lastthree[3], aalg[AH_HMAC_MAXHASHLEN];
- int hlen, skip, protoff, error, alen;
+ const struct auth_hash *esph;
+ const struct enc_xform *espx;
struct mbuf *m;
struct cryptodesc *crd;
- struct auth_hash *esph;
- struct enc_xform *espx;
- struct tdb_crypto *tc;
+ struct xform_data *xd;
struct secasvar *sav;
struct secasindex *saidx;
caddr_t ptr;
+ uint64_t cryptoid;
+ int hlen, skip, protoff, error, alen;
crd = crp->crp_desc;
IPSEC_ASSERT(crd != NULL, ("null crypto descriptor!"));
- tc = (struct tdb_crypto *) crp->crp_opaque;
- IPSEC_ASSERT(tc != NULL, ("null opaque crypto data area!"));
- skip = tc->tc_skip;
- protoff = tc->tc_protoff;
m = (struct mbuf *) crp->crp_buf;
-
- sav = tc->tc_sav;
- IPSEC_ASSERT(sav != NULL, ("null SA!"));
-
+ xd = (struct xform_data *) crp->crp_opaque;
+ sav = xd->sav;
+ skip = xd->skip;
+ protoff = xd->protoff;
+ cryptoid = xd->cryptoid;
saidx = &sav->sah->saidx;
- IPSEC_ASSERT(saidx->dst.sa.sa_family == AF_INET ||
- saidx->dst.sa.sa_family == AF_INET6,
- ("unexpected protocol family %u", saidx->dst.sa.sa_family));
-
esph = sav->tdb_authalgxform;
espx = sav->tdb_encalgxform;
/* Check for crypto errors */
if (crp->crp_etype) {
- /* Reset the session ID */
- if (sav->tdb_cryptoid != 0)
- sav->tdb_cryptoid = crp->crp_sid;
-
- if (crp->crp_etype == EAGAIN)
+ if (crp->crp_etype == EAGAIN) {
+ /* Reset the session ID */
+ if (ipsec_updateid(sav, &crp->crp_sid, &cryptoid) != 0)
+ crypto_freesession(cryptoid);
+ xd->cryptoid = crp->crp_sid;
return (crypto_dispatch(crp));
-
+ }
ESPSTAT_INC(esps_noxform);
DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
error = crp->crp_etype;
@@ -527,7 +489,7 @@ esp_input_cb(struct cryptop *crp)
AHSTAT_INC(ahs_hist[sav->alg_auth]);
/* Copy the authenticator from the packet */
m_copydata(m, m->m_pkthdr.len - alen, alen, aalg);
- ptr = (caddr_t) (tc + 1);
+ ptr = (caddr_t) (xd + 1);
/* Verify authenticator */
if (timingsafe_bcmp(ptr, aalg, alen) != 0) {
@@ -539,13 +501,13 @@ esp_input_cb(struct cryptop *crp)
error = EACCES;
goto bad;
}
-
+ m->m_flags |= M_AUTHIPDGM;
/* Remove trailing authenticator */
m_adj(m, -alen);
}
/* Release the crypto descriptors */
- free(tc, M_XDATA), tc = NULL;
+ free(xd, M_XDATA), xd = NULL;
crypto_freereq(crp), crp = NULL;
/*
@@ -561,13 +523,16 @@ esp_input_cb(struct cryptop *crp)
m_copydata(m, skip + offsetof(struct newesp, esp_seq),
sizeof (seq), (caddr_t) &seq);
+ SECASVAR_LOCK(sav);
if (ipsec_updatereplay(ntohl(seq), sav)) {
+ SECASVAR_UNLOCK(sav);
DPRINTF(("%s: packet replay check for %s\n", __func__,
- ipsec_logsastr(sav, buf, sizeof(buf))));
+ ipsec_sa2str(sav, buf, sizeof(buf))));
ESPSTAT_INC(esps_replay);
- error = ENOBUFS;
+ error = EACCES;
goto bad;
}
+ SECASVAR_UNLOCK(sav);
}
/* Determine the ESP header length */
@@ -635,46 +600,40 @@ esp_input_cb(struct cryptop *crp)
panic("%s: Unexpected address family: %d saidx=%p", __func__,
saidx->dst.sa.sa_family, saidx);
}
-
- KEY_FREESAV(&sav);
return error;
bad:
- if (sav)
- KEY_FREESAV(&sav);
+ if (sav != NULL)
+ key_freesav(&sav);
if (m != NULL)
m_freem(m);
- if (tc != NULL)
- free(tc, M_XDATA);
+ if (xd != NULL)
+ free(xd, M_XDATA);
if (crp != NULL)
crypto_freereq(crp);
return error;
}
-
/*
- * ESP output routine, called by ipsec[46]_process_packet().
+ * ESP output routine, called by ipsec[46]_perform_request().
*/
static int
-esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
- int skip, int protoff)
+esp_output(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
+ u_int idx, int skip, int protoff)
{
- char buf[INET6_ADDRSTRLEN];
- struct enc_xform *espx;
- struct auth_hash *esph;
- uint8_t *ivp;
- uint64_t cntr;
- int hlen, rlen, padding, blks, alen, i, roff;
- struct mbuf *mo = (struct mbuf *) NULL;
- struct tdb_crypto *tc;
- struct secasvar *sav;
+ char buf[IPSEC_ADDRSTRLEN];
+ struct cryptodesc *crde = NULL, *crda = NULL;
+ struct cryptop *crp;
+ const struct auth_hash *esph;
+ const struct enc_xform *espx;
+ struct mbuf *mo = NULL;
+ struct xform_data *xd;
struct secasindex *saidx;
unsigned char *pad;
- u_int8_t prot;
+ uint8_t *ivp;
+ uint64_t cntr, cryptoid;
+ int hlen, rlen, padding, blks, alen, i, roff;
int error, maxpacketsize;
+ uint8_t prot;
- struct cryptodesc *crde = NULL, *crda = NULL;
- struct cryptop *crp;
-
- sav = isr->sav;
IPSEC_ASSERT(sav != NULL, ("null SA"));
esph = sav->tdb_authalgxform;
espx = sav->tdb_encalgxform;
@@ -720,8 +679,9 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
error = EPFNOSUPPORT;
goto bad;
}
+ /*
DPRINTF(("%s: skip %d hlen %d rlen %d padding %d alen %d blksd %d\n",
- __func__, skip, hlen, rlen, padding, alen, blks));
+ __func__, skip, hlen, rlen, padding, alen, blks)); */
if (skip + hlen + rlen + padding + alen > maxpacketsize) {
DPRINTF(("%s: packet in SA %s/%08lx got too big "
"(len %u, max len %u)\n", __func__,
@@ -752,15 +712,17 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
DPRINTF(("%s: %u byte ESP hdr inject failed for SA %s/%08lx\n",
__func__, hlen, ipsec_address(&saidx->dst, buf,
sizeof(buf)), (u_long) ntohl(sav->spi)));
- ESPSTAT_INC(esps_hdrops); /* XXX diffs from openbsd */
+ ESPSTAT_INC(esps_hdrops); /* XXX diffs from openbsd */
error = ENOBUFS;
goto bad;
}
/* Initialize ESP header. */
- bcopy((caddr_t) &sav->spi, mtod(mo, caddr_t) + roff, sizeof(u_int32_t));
+ bcopy((caddr_t) &sav->spi, mtod(mo, caddr_t) + roff,
+ sizeof(uint32_t));
+ SECASVAR_LOCK(sav);
if (sav->replay) {
- u_int32_t replay;
+ uint32_t replay;
#ifdef REGRESSION
/* Emulate replay attack when ipsec_replay is TRUE. */
@@ -768,10 +730,14 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
#endif
sav->replay->count++;
replay = htonl(sav->replay->count);
- bcopy((caddr_t) &replay,
- mtod(mo, caddr_t) + roff + sizeof(u_int32_t),
- sizeof(u_int32_t));
+
+ bcopy((caddr_t) &replay, mtod(mo, caddr_t) + roff +
+ sizeof(uint32_t), sizeof(uint32_t));
}
+ cryptoid = sav->tdb_cryptoid;
+ if (SAV_ISCTRORGCM(sav))
+ cntr = sav->cntr++;
+ SECASVAR_UNLOCK(sav);
/*
* Add padding -- better to do it ourselves than use the crypto engine,
@@ -823,11 +789,10 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
}
/* IPsec-specific opaque crypto info. */
- tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto),
- M_XDATA, M_NOWAIT|M_ZERO);
- if (tc == NULL) {
+ xd = malloc(sizeof(struct xform_data), M_XDATA, M_NOWAIT | M_ZERO);
+ if (xd == NULL) {
crypto_freereq(crp);
- DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__));
+ DPRINTF(("%s: failed to allocate xform_data\n", __func__));
ESPSTAT_INC(esps_crypto);
error = ENOBUFS;
goto bad;
@@ -853,13 +818,10 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
/* Nonce is last four bytes of key, RFC3686 5.1 */
memcpy(ivp, sav->key_enc->key_data +
_KEYLEN(sav->key_enc) - 4, 4);
- SECASVAR_LOCK(sav);
- cntr = sav->cntr++;
- SECASVAR_UNLOCK(sav);
be64enc(&ivp[4], cntr);
-
if (SAV_ISCTR(sav)) {
/* Initial block counter is 1, RFC3686 4 */
+ /* XXXAE: should we use this only for first packet? */
be32enc(&ivp[sav->ivlen + 4], 1);
}
@@ -868,21 +830,18 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
}
/* Callback parameters */
- key_addref(isr->sp);
- 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;
+ xd->sp = sp;
+ xd->sav = sav;
+ xd->idx = idx;
+ xd->cryptoid = cryptoid;
/* Crypto operation descriptor. */
crp->crp_ilen = m->m_pkthdr.len; /* Total input length. */
crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC;
crp->crp_buf = (caddr_t) m;
crp->crp_callback = esp_output_cb;
- crp->crp_opaque = (caddr_t) tc;
- crp->crp_sid = sav->tdb_cryptoid;
+ crp->crp_opaque = (caddr_t) xd;
+ crp->crp_sid = cryptoid;
if (esph) {
/* Authentication descriptor. */
@@ -901,53 +860,40 @@ bad:
m_freem(m);
return (error);
}
-
/*
* ESP output callback from the crypto driver.
*/
static int
esp_output_cb(struct cryptop *crp)
{
- char buf[INET6_ADDRSTRLEN];
- struct tdb_crypto *tc;
- struct ipsecrequest *isr;
+ struct xform_data *xd;
+ struct secpolicy *sp;
struct secasvar *sav;
struct mbuf *m;
+ uint64_t cryptoid;
+ u_int idx;
int error;
- tc = (struct tdb_crypto *) crp->crp_opaque;
- IPSEC_ASSERT(tc != NULL, ("null opaque data area!"));
+ xd = (struct xform_data *) crp->crp_opaque;
m = (struct mbuf *) crp->crp_buf;
-
- isr = tc->tc_isr;
- IPSEC_ASSERT(isr->sp != NULL, ("NULL isr->sp"));
- IPSECREQUEST_LOCK(isr);
- sav = tc->tc_sav;
-
- /* With the isr lock released, SA pointer may have changed. */
- if (sav != isr->sav) {
- ESPSTAT_INC(esps_notdb);
- DPRINTF(("%s: SA gone during crypto (SA %s/%08lx proto %u)\n",
- __func__, ipsec_address(&tc->tc_dst, buf, sizeof(buf)),
- (u_long) ntohl(tc->tc_spi), tc->tc_proto));
- error = ENOBUFS; /*XXX*/
- goto bad;
- }
+ sp = xd->sp;
+ sav = xd->sav;
+ idx = xd->idx;
+ cryptoid = xd->cryptoid;
/* Check for crypto errors. */
if (crp->crp_etype) {
- /* Reset session ID. */
- if (sav->tdb_cryptoid != 0)
- sav->tdb_cryptoid = crp->crp_sid;
-
if (crp->crp_etype == EAGAIN) {
- IPSECREQUEST_UNLOCK(isr);
+ /* Reset the session ID */
+ if (ipsec_updateid(sav, &crp->crp_sid, &cryptoid) != 0)
+ crypto_freesession(cryptoid);
+ xd->cryptoid = crp->crp_sid;
return (crypto_dispatch(crp));
}
-
ESPSTAT_INC(esps_noxform);
DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
error = crp->crp_etype;
+ m_freem(m);
goto bad;
}
@@ -958,19 +904,17 @@ esp_output_cb(struct cryptop *crp)
error = EINVAL;
goto bad;
}
+ free(xd, M_XDATA);
+ crypto_freereq(crp);
ESPSTAT_INC(esps_hist[sav->alg_enc]);
if (sav->tdb_authalgxform != NULL)
AHSTAT_INC(ahs_hist[sav->alg_auth]);
- /* Release crypto descriptors. */
- free(tc, M_XDATA);
- crypto_freereq(crp);
-
#ifdef REGRESSION
/* Emulate man-in-the-middle attack when ipsec_integrity is TRUE. */
if (V_ipsec_integrity) {
static unsigned char ipseczeroes[AH_HMAC_MAXHASHLEN];
- struct auth_hash *esph;
+ const struct auth_hash *esph;
/*
* Corrupt HMAC if we want to test integrity verification of
@@ -988,33 +932,26 @@ esp_output_cb(struct cryptop *crp)
#endif
/* NB: m is reclaimed by ipsec_process_done. */
- error = ipsec_process_done(m, isr);
- KEY_FREESAV(&sav);
- IPSECREQUEST_UNLOCK(isr);
- KEY_FREESP(&isr->sp);
+ error = ipsec_process_done(m, sp, sav, idx);
return (error);
bad:
- if (sav)
- KEY_FREESAV(&sav);
- IPSECREQUEST_UNLOCK(isr);
- KEY_FREESP(&isr->sp);
- if (m)
- m_freem(m);
- free(tc, M_XDATA);
+ free(xd, M_XDATA);
crypto_freereq(crp);
+ key_freesav(&sav);
+ key_freesp(&sp);
return (error);
}
static struct xformsw esp_xformsw = {
- XF_ESP, XFT_CONF|XFT_AUTH, "IPsec ESP",
- esp_init, esp_zeroize, esp_input,
- esp_output
+ .xf_type = XF_ESP,
+ .xf_name = "IPsec ESP",
+ .xf_init = esp_init,
+ .xf_zeroize = esp_zeroize,
+ .xf_input = esp_input,
+ .xf_output = esp_output,
};
-static void
-esp_attach(void)
-{
-
- xform_register(&esp_xformsw);
-}
-SYSINIT(esp_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, esp_attach, NULL);
+SYSINIT(esp_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE,
+ xform_attach, &esp_xformsw);
+SYSUNINIT(esp_xform_uninit, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE,
+ xform_detach, &esp_xformsw);
OpenPOWER on IntegriCloud