Add support for IPsec ESN and pass relevant information to crypto layer

Implement support for including IPsec ESN (Extended Sequence Number) to
both encrypt and authenticate mode (eg. AES-CBC and SHA256) and combined
mode (eg. AES-GCM). Both ESP and AH protocols are updated. Additionally
pass relevant information about ESN to crypto layer.

For the ETA mode the ESN is stored in separate crp_esn buffer because
the high-order 32 bits of the sequence number are appended after the
Next Header (RFC 4303).

For the AEAD modes the high-order 32 bits of the sequence number
[e.g.  RFC 4106, Chapter 5 AAD Construction] are included as part of
crp_aad (SPI + ESN (32 high order bits) + Seq nr (32 low order bits)).

Submitted by:           Grzegorz Jaszczyk <jaz@semihalf.com>
                        Patryk Duda <pdk@semihalf.com>
Reviewed by:            jhb, gnn
Differential revision:  https://reviews.freebsd.org/D22369
Obtained from:          Semihalf
Sponsored by:           Stormshield
This commit is contained in:
Marcin Wojtas 2020-10-16 11:25:45 +00:00
parent 8b7f39947c
commit 4d36d1fd59
3 changed files with 122 additions and 14 deletions

View File

@ -197,6 +197,8 @@ struct secasvar {
#define SAV_ISCTR(_sav) ((_sav)->alg_enc == SADB_X_EALG_AESCTR) #define SAV_ISCTR(_sav) ((_sav)->alg_enc == SADB_X_EALG_AESCTR)
#define SAV_ISCTRORGCM(_sav) (SAV_ISCTR((_sav)) || SAV_ISGCM((_sav))) #define SAV_ISCTRORGCM(_sav) (SAV_ISCTR((_sav)) || SAV_ISGCM((_sav)))
#define IPSEC_SEQH_SHIFT 32
/* Replay prevention, protected by SECASVAR_LOCK: /* Replay prevention, protected by SECASVAR_LOCK:
* (m) locked by mtx * (m) locked by mtx
* (c) read only except during creation / free * (c) read only except during creation / free

View File

@ -236,6 +236,10 @@ ah_init(struct secasvar *sav, struct xformsw *xsp)
memset(&csp, 0, sizeof(csp)); memset(&csp, 0, sizeof(csp));
csp.csp_mode = CSP_MODE_DIGEST; csp.csp_mode = CSP_MODE_DIGEST;
if (sav->flags & SADB_X_SAFLAGS_ESN)
csp.csp_flags |= CSP_F_ESN;
error = ah_init0(sav, xsp, &csp); error = ah_init0(sav, xsp, &csp);
return error ? error : return error ? error :
crypto_newsession(&sav->tdb_cryptoid, &csp, V_crypto_support); crypto_newsession(&sav->tdb_cryptoid, &csp, V_crypto_support);
@ -654,6 +658,12 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
crp->crp_callback = ah_input_cb; crp->crp_callback = ah_input_cb;
crp->crp_opaque = xd; crp->crp_opaque = xd;
if (sav->flags & SADB_X_SAFLAGS_ESN &&
sav->replay != NULL && sav->replay->wsize != 0) {
seqh = htonl(seqh);
memcpy(crp->crp_esn, &seqh, sizeof(seqh));
}
/* These are passed as-is to the callback. */ /* These are passed as-is to the callback. */
xd->sav = sav; xd->sav = sav;
xd->nxt = hl; xd->nxt = hl;
@ -834,6 +844,7 @@ ah_output(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
uint16_t iplen; uint16_t iplen;
int error, rplen, authsize, ahsize, maxpacketsize, roff; int error, rplen, authsize, ahsize, maxpacketsize, roff;
uint8_t prot; uint8_t prot;
uint32_t seqh;
IPSEC_ASSERT(sav != NULL, ("null SA")); IPSEC_ASSERT(sav != NULL, ("null SA"));
ahx = sav->tdb_authalgxform; ahx = sav->tdb_authalgxform;
@ -1031,6 +1042,11 @@ ah_output(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
crp->crp_callback = ah_output_cb; crp->crp_callback = ah_output_cb;
crp->crp_opaque = xd; crp->crp_opaque = xd;
if (sav->flags & SADB_X_SAFLAGS_ESN && sav->replay != NULL) {
seqh = htonl((uint32_t)(sav->replay->count >> IPSEC_SEQH_SHIFT));
memcpy(crp->crp_esn, &seqh, sizeof(seqh));
}
/* These are passed as-is to the callback. */ /* These are passed as-is to the callback. */
xd->sp = sp; xd->sp = sp;
xd->sav = sav; xd->sav = sav;

View File

@ -80,6 +80,8 @@
#include <opencrypto/cryptodev.h> #include <opencrypto/cryptodev.h>
#include <opencrypto/xform.h> #include <opencrypto/xform.h>
#define SPI_SIZE 4
VNET_DEFINE(int, esp_enable) = 1; VNET_DEFINE(int, esp_enable) = 1;
VNET_DEFINE_STATIC(int, esp_ctr_compatibility) = 1; VNET_DEFINE_STATIC(int, esp_ctr_compatibility) = 1;
#define V_esp_ctr_compatibility VNET(esp_ctr_compatibility) #define V_esp_ctr_compatibility VNET(esp_ctr_compatibility)
@ -219,9 +221,13 @@ esp_init(struct secasvar *sav, struct xformsw *xsp)
return EINVAL; return EINVAL;
} }
csp.csp_mode = CSP_MODE_AEAD; csp.csp_mode = CSP_MODE_AEAD;
} else if (sav->alg_auth != 0) if (sav->flags & SADB_X_SAFLAGS_ESN)
csp.csp_flags |= CSP_F_SEPARATE_AAD;
} else if (sav->alg_auth != 0) {
csp.csp_mode = CSP_MODE_ETA; csp.csp_mode = CSP_MODE_ETA;
else if (sav->flags & SADB_X_SAFLAGS_ESN)
csp.csp_flags |= CSP_F_ESN;
} else
csp.csp_mode = CSP_MODE_CIPHER; csp.csp_mode = CSP_MODE_CIPHER;
/* Initialize crypto session. */ /* Initialize crypto session. */
@ -263,6 +269,7 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
crypto_session_t cryptoid; crypto_session_t cryptoid;
int alen, error, hlen, plen; int alen, error, hlen, plen;
uint32_t seqh; uint32_t seqh;
const struct crypto_session_params *csp;
IPSEC_ASSERT(sav != NULL, ("null SA")); IPSEC_ASSERT(sav != NULL, ("null SA"));
IPSEC_ASSERT(sav->tdb_encalgxform != NULL, ("null encoding xform")); IPSEC_ASSERT(sav->tdb_encalgxform != NULL, ("null encoding xform"));
@ -329,6 +336,7 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
error = EACCES; error = EACCES;
goto bad; goto bad;
} }
seqh = htonl(seqh);
} }
cryptoid = sav->tdb_cryptoid; cryptoid = sav->tdb_cryptoid;
SECASVAR_UNLOCK(sav); SECASVAR_UNLOCK(sav);
@ -350,19 +358,49 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
xd = malloc(sizeof(*xd), M_XDATA, M_NOWAIT | M_ZERO); xd = malloc(sizeof(*xd), M_XDATA, M_NOWAIT | M_ZERO);
if (xd == NULL) { if (xd == NULL) {
DPRINTF(("%s: failed to allocate xform_data\n", __func__)); DPRINTF(("%s: failed to allocate xform_data\n", __func__));
ESPSTAT_INC(esps_crypto); goto xd_fail;
crypto_freereq(crp);
error = ENOBUFS;
goto bad;
} }
if (esph != NULL) { if (esph != NULL) {
crp->crp_op = CRYPTO_OP_VERIFY_DIGEST; crp->crp_op = CRYPTO_OP_VERIFY_DIGEST;
crp->crp_aad_start = skip;
if (SAV_ISGCM(sav)) if (SAV_ISGCM(sav))
crp->crp_aad_length = 8; /* RFC4106 5, SPI + SN */ crp->crp_aad_length = 8; /* RFC4106 5, SPI + SN */
else else
crp->crp_aad_length = hlen; crp->crp_aad_length = hlen;
csp = crypto_get_params(crp->crp_session);
if ((csp->csp_flags & CSP_F_SEPARATE_AAD) &&
(sav->replay != NULL) && (sav->replay->wsize != 0)) {
int aad_skip;
crp->crp_aad_length += sizeof(seqh);
crp->crp_aad = malloc(crp->crp_aad_length, M_XDATA, M_NOWAIT);
if (crp->crp_aad == NULL) {
DPRINTF(("%s: failed to allocate xform_data\n",
__func__));
goto crp_aad_fail;
}
/* SPI */
m_copydata(m, skip, SPI_SIZE, crp->crp_aad);
aad_skip = SPI_SIZE;
/* ESN */
bcopy(&seqh, (char *)crp->crp_aad + aad_skip, sizeof(seqh));
aad_skip += sizeof(seqh);
/* Rest of aad */
if (crp->crp_aad_length - aad_skip > 0)
m_copydata(m, skip + SPI_SIZE,
crp->crp_aad_length - aad_skip,
(char *)crp->crp_aad + aad_skip);
} else
crp->crp_aad_start = skip;
if (csp->csp_flags & CSP_F_ESN &&
sav->replay != NULL && sav->replay->wsize != 0)
memcpy(crp->crp_esn, &seqh, sizeof(seqh));
crp->crp_digest_start = m->m_pkthdr.len - alen; crp->crp_digest_start = m->m_pkthdr.len - alen;
} }
@ -423,6 +461,13 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
crp->crp_iv_start = skip + hlen - sav->ivlen; crp->crp_iv_start = skip + hlen - sav->ivlen;
return (crypto_dispatch(crp)); return (crypto_dispatch(crp));
crp_aad_fail:
free(xd, M_XDATA);
xd_fail:
crypto_freereq(crp);
ESPSTAT_INC(esps_crypto);
error = ENOBUFS;
bad: bad:
m_freem(m); m_freem(m);
key_freesav(&sav); key_freesav(&sav);
@ -505,6 +550,7 @@ esp_input_cb(struct cryptop *crp)
/* Release the crypto descriptors */ /* Release the crypto descriptors */
free(xd, M_XDATA), xd = NULL; free(xd, M_XDATA), xd = NULL;
free(crp->crp_aad, M_XDATA), crp->crp_aad = NULL;
crypto_freereq(crp), crp = NULL; crypto_freereq(crp), crp = NULL;
/* /*
@ -614,8 +660,10 @@ esp_input_cb(struct cryptop *crp)
m_freem(m); m_freem(m);
if (xd != NULL) if (xd != NULL)
free(xd, M_XDATA); free(xd, M_XDATA);
if (crp != NULL) if (crp != NULL) {
free(crp->crp_aad, M_XDATA);
crypto_freereq(crp); crypto_freereq(crp);
}
return error; return error;
} }
/* /*
@ -639,6 +687,8 @@ esp_output(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
int hlen, rlen, padding, blks, alen, i, roff; int hlen, rlen, padding, blks, alen, i, roff;
int error, maxpacketsize; int error, maxpacketsize;
uint8_t prot; uint8_t prot;
uint32_t seqh;
const struct crypto_session_params *csp;
IPSEC_ASSERT(sav != NULL, ("null SA")); IPSEC_ASSERT(sav != NULL, ("null SA"));
esph = sav->tdb_authalgxform; esph = sav->tdb_authalgxform;
@ -745,6 +795,8 @@ esp_output(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
bcopy((caddr_t) &replay, mtod(mo, caddr_t) + roff + bcopy((caddr_t) &replay, mtod(mo, caddr_t) + roff +
sizeof(uint32_t), sizeof(uint32_t)); sizeof(uint32_t), sizeof(uint32_t));
seqh = htonl((uint32_t)(sav->replay->count >> IPSEC_SEQH_SHIFT));
} }
cryptoid = sav->tdb_cryptoid; cryptoid = sav->tdb_cryptoid;
if (SAV_ISCTRORGCM(sav)) if (SAV_ISCTRORGCM(sav))
@ -801,13 +853,10 @@ esp_output(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
} }
/* IPsec-specific opaque crypto info. */ /* IPsec-specific opaque crypto info. */
xd = malloc(sizeof(struct xform_data), M_XDATA, M_NOWAIT | M_ZERO); xd = malloc(sizeof(struct xform_data), M_XDATA, M_NOWAIT | M_ZERO);
if (xd == NULL) { if (xd == NULL) {
crypto_freereq(crp);
DPRINTF(("%s: failed to allocate xform_data\n", __func__)); DPRINTF(("%s: failed to allocate xform_data\n", __func__));
ESPSTAT_INC(esps_crypto); goto xd_fail;
error = ENOBUFS;
goto bad;
} }
/* Encryption descriptor. */ /* Encryption descriptor. */
@ -855,15 +904,54 @@ esp_output(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
if (esph) { if (esph) {
/* Authentication descriptor. */ /* Authentication descriptor. */
crp->crp_op |= CRYPTO_OP_COMPUTE_DIGEST; crp->crp_op |= CRYPTO_OP_COMPUTE_DIGEST;
crp->crp_aad_start = skip;
if (SAV_ISGCM(sav)) if (SAV_ISGCM(sav))
crp->crp_aad_length = 8; /* RFC4106 5, SPI + SN */ crp->crp_aad_length = 8; /* RFC4106 5, SPI + SN */
else else
crp->crp_aad_length = hlen; crp->crp_aad_length = hlen;
csp = crypto_get_params(crp->crp_session);
if (csp->csp_flags & CSP_F_SEPARATE_AAD &&
sav->replay != NULL) {
int aad_skip;
crp->crp_aad_length += sizeof(seqh);
crp->crp_aad = malloc(crp->crp_aad_length, M_XDATA, M_NOWAIT);
if (crp->crp_aad == NULL) {
DPRINTF(("%s: failed to allocate xform_data\n",
__func__));
goto crp_aad_fail;
}
/* SPI */
m_copydata(m, skip, SPI_SIZE, crp->crp_aad);
aad_skip = SPI_SIZE;
/* ESN */
bcopy(&seqh, (char *)crp->crp_aad + aad_skip, sizeof(seqh));
aad_skip += sizeof(seqh);
/* Rest of aad */
if (crp->crp_aad_length - aad_skip > 0)
m_copydata(m, skip + SPI_SIZE,
crp->crp_aad_length - aad_skip,
(char *)crp->crp_aad + aad_skip);
} else
crp->crp_aad_start = skip;
if (csp->csp_flags & CSP_F_ESN && sav->replay != NULL)
memcpy(crp->crp_esn, &seqh, sizeof(seqh));
crp->crp_digest_start = m->m_pkthdr.len - alen; crp->crp_digest_start = m->m_pkthdr.len - alen;
} }
return crypto_dispatch(crp); return crypto_dispatch(crp);
crp_aad_fail:
free(xd, M_XDATA);
xd_fail:
crypto_freereq(crp);
ESPSTAT_INC(esps_crypto);
error = ENOBUFS;
bad: bad:
if (m) if (m)
m_freem(m); m_freem(m);
@ -918,6 +1006,7 @@ esp_output_cb(struct cryptop *crp)
goto bad; goto bad;
} }
free(xd, M_XDATA); free(xd, M_XDATA);
free(crp->crp_aad, M_XDATA);
crypto_freereq(crp); crypto_freereq(crp);
ESPSTAT_INC(esps_hist[sav->alg_enc]); ESPSTAT_INC(esps_hist[sav->alg_enc]);
if (sav->tdb_authalgxform != NULL) if (sav->tdb_authalgxform != NULL)
@ -951,6 +1040,7 @@ esp_output_cb(struct cryptop *crp)
bad: bad:
CURVNET_RESTORE(); CURVNET_RESTORE();
free(xd, M_XDATA); free(xd, M_XDATA);
free(crp->crp_aad, M_XDATA);
crypto_freereq(crp); crypto_freereq(crp);
key_freesav(&sav); key_freesav(&sav);
key_freesp(&sp); key_freesp(&sp);