summaryrefslogtreecommitdiffstats
path: root/sys/netipsec
diff options
context:
space:
mode:
authorjmg <jmg@FreeBSD.org>2015-08-04 17:47:11 +0000
committerjmg <jmg@FreeBSD.org>2015-08-04 17:47:11 +0000
commit20a77876c747677e14860ccb77e37ba572e7b1f2 (patch)
tree90da08143badbf5312e624a76e8aa042d2715ade /sys/netipsec
parent97d3fe874dc469ad842c648127f280946d3c08ed (diff)
downloadFreeBSD-src-20a77876c747677e14860ccb77e37ba572e7b1f2.zip
FreeBSD-src-20a77876c747677e14860ccb77e37ba572e7b1f2.tar.gz
Make IPsec work with AES-GCM and AES-ICM (aka CTR) in OCF... IPsec
defines the keys differently than NIST does, so we have to muck with key lengths and nonce/IVs to be standard compliant... Remove the iv from secasvar as it was unused... Add a counter protected by a mutex to ensure that the counter for GCM and ICM will never be repeated.. This is a requirement for security.. I would use atomics, but we don't have a 64bit one on all platforms.. Fix a bug where IPsec was depending upon the OCF to ensure that the blocksize was always at least 4 bytes to maintain alignment... Move this logic into IPsec so changes to OCF won't break IPsec... In one place, espx was always non-NULL, so don't test that it's non-NULL before doing work.. minor style cleanups... drop setting key and klen as they were not used... Enforce that OCF won't pass invalid key lengths to AES that would panic the machine... This was has been tested by others too... I tested this against NetBSD 6.1.5 using mini-test suite in https://github.com/jmgurney/ipseccfgs and the only things that don't pass are keyed md5 and sha1, and 3des-deriv (setkey syntax error), all other modes listed in setkey's man page... The nice thing is that NetBSD uses setkey, so same config files were used on both... Reviewed by: gnn
Diffstat (limited to 'sys/netipsec')
-rw-r--r--sys/netipsec/key.c2
-rw-r--r--sys/netipsec/key_debug.c5
-rw-r--r--sys/netipsec/keydb.h8
-rw-r--r--sys/netipsec/xform_esp.c170
4 files changed, 96 insertions, 89 deletions
diff --git a/sys/netipsec/key.c b/sys/netipsec/key.c
index 34e8c41..9927509 100644
--- a/sys/netipsec/key.c
+++ b/sys/netipsec/key.c
@@ -2835,7 +2835,6 @@ key_cleansav(struct secasvar *sav)
sav->tdb_xform->xf_zeroize(sav);
sav->tdb_xform = NULL;
} else {
- KASSERT(sav->iv == NULL, ("iv but no xform"));
if (sav->key_auth != NULL)
bzero(sav->key_auth->key_data, _KEYLEN(sav->key_auth));
if (sav->key_enc != NULL)
@@ -3013,7 +3012,6 @@ key_setsaval(struct secasvar *sav, struct mbuf *m,
sav->key_enc = NULL;
sav->sched = NULL;
sav->schedlen = 0;
- sav->iv = NULL;
sav->lft_c = NULL;
sav->lft_h = NULL;
sav->lft_s = NULL;
diff --git a/sys/netipsec/key_debug.c b/sys/netipsec/key_debug.c
index 97ac061..b5bdb0e 100644
--- a/sys/netipsec/key_debug.c
+++ b/sys/netipsec/key_debug.c
@@ -577,11 +577,6 @@ kdebug_secasv(struct secasvar *sav)
kdebug_sadb_key((struct sadb_ext *)sav->key_auth);
if (sav->key_enc != NULL)
kdebug_sadb_key((struct sadb_ext *)sav->key_enc);
- if (sav->iv != NULL) {
- printf(" iv=");
- ipsec_hexdump(sav->iv, sav->ivlen ? sav->ivlen : 8);
- printf("\n");
- }
if (sav->replay != NULL)
kdebug_secreplay(sav->replay);
diff --git a/sys/netipsec/keydb.h b/sys/netipsec/keydb.h
index 15dbc9c..3fe28eb 100644
--- a/sys/netipsec/keydb.h
+++ b/sys/netipsec/keydb.h
@@ -122,10 +122,10 @@ struct secasvar {
struct seckey *key_auth; /* Key for Authentication */
struct seckey *key_enc; /* Key for Encryption */
- caddr_t iv; /* Initilization Vector */
u_int ivlen; /* length of IV */
void *sched; /* intermediate encryption key */
size_t schedlen;
+ uint64_t cntr; /* counter for GCM and CTR */
struct secreplay *replay; /* replay prevention */
time_t created; /* for lifetime */
@@ -163,6 +163,12 @@ struct secasvar {
#define SECASVAR_UNLOCK(_sav) mtx_unlock(&(_sav)->lock)
#define SECASVAR_LOCK_DESTROY(_sav) mtx_destroy(&(_sav)->lock)
#define SECASVAR_LOCK_ASSERT(_sav) mtx_assert(&(_sav)->lock, MA_OWNED)
+#define SAV_ISGCM(_sav) \
+ ((_sav)->alg_enc == SADB_X_EALG_AESGCM8 || \
+ (_sav)->alg_enc == SADB_X_EALG_AESGCM12 || \
+ (_sav)->alg_enc == SADB_X_EALG_AESGCM16)
+#define SAV_ISCTR(_sav) ((_sav)->alg_enc == SADB_X_EALG_AESCTR)
+#define SAV_ISCTRORGCM(_sav) (SAV_ISCTR((_sav)) || SAV_ISGCM((_sav)))
/* replay prevention */
struct secreplay {
diff --git a/sys/netipsec/xform_esp.c b/sys/netipsec/xform_esp.c
index d8182df..a48c038 100644
--- a/sys/netipsec/xform_esp.c
+++ b/sys/netipsec/xform_esp.c
@@ -48,6 +48,8 @@
#include <sys/random.h>
#include <sys/rwlock.h>
#include <sys/sysctl.h>
+#include <sys/mutex.h>
+#include <machine/atomic.h>
#include <net/if.h>
#include <net/vnet.h>
@@ -182,12 +184,14 @@ esp_init(struct secasvar *sav, struct xformsw *xsp)
__func__, txform->name));
return EINVAL;
}
- if ((sav->flags&(SADB_X_EXT_OLD|SADB_X_EXT_IV4B)) == SADB_X_EXT_IV4B) {
+ if ((sav->flags & (SADB_X_EXT_OLD | SADB_X_EXT_IV4B)) ==
+ SADB_X_EXT_IV4B) {
DPRINTF(("%s: 4-byte IV not supported with protocol\n",
__func__));
return EINVAL;
}
- keylen = _KEYLEN(sav->key_enc);
+ /* subtract off the salt, RFC4106, 8.1 and RFC3686, 5.1 */
+ keylen = _KEYLEN(sav->key_enc) - SAV_ISCTRORGCM(sav) * 4;
if (txform->minkey > keylen || keylen > txform->maxkey) {
DPRINTF(("%s: invalid key length %u, must be in the range "
"[%u..%u] for algorithm %s\n", __func__,
@@ -202,9 +206,10 @@ esp_init(struct secasvar *sav, struct xformsw *xsp)
* the ESP header will be processed incorrectly. The
* compromise is to force it to zero here.
*/
- sav->ivlen = (txform == &enc_xform_null ? 0 : txform->ivsize);
- sav->iv = (caddr_t) malloc(sav->ivlen, M_XDATA, M_WAITOK);
- key_randomfill(sav->iv, sav->ivlen); /*XXX*/
+ if (SAV_ISCTRORGCM(sav))
+ sav->ivlen = 8; /* RFC4106 3.1 and RFC3686 3.1 */
+ else
+ sav->ivlen = (txform == &enc_xform_null ? 0 : txform->ivsize);
/*
* Setup AH-related state.
@@ -226,15 +231,15 @@ esp_init(struct secasvar *sav, struct xformsw *xsp)
*/
if (sav->alg_enc == SADB_X_EALG_AESGCM16) {
switch (keylen) {
- case AES_128_HMAC_KEY_LEN:
+ case AES_128_GMAC_KEY_LEN:
sav->alg_auth = SADB_X_AALG_AES128GMAC;
sav->tdb_authalgxform = &auth_hash_nist_gmac_aes_128;
break;
- case AES_192_HMAC_KEY_LEN:
+ case AES_192_GMAC_KEY_LEN:
sav->alg_auth = SADB_X_AALG_AES192GMAC;
sav->tdb_authalgxform = &auth_hash_nist_gmac_aes_192;
break;
- case AES_256_HMAC_KEY_LEN:
+ case AES_256_GMAC_KEY_LEN:
sav->alg_auth = SADB_X_AALG_AES256GMAC;
sav->tdb_authalgxform = &auth_hash_nist_gmac_aes_256;
break;
@@ -246,19 +251,15 @@ esp_init(struct secasvar *sav, struct xformsw *xsp)
}
bzero(&cria, sizeof(cria));
cria.cri_alg = sav->tdb_authalgxform->type;
- cria.cri_klen = _KEYBITS(sav->key_enc) + 4;
cria.cri_key = sav->key_enc->key_data;
+ cria.cri_klen = _KEYBITS(sav->key_enc) - SAV_ISGCM(sav) * 32;
}
/* Initialize crypto session. */
- bzero(&crie, sizeof (crie));
+ bzero(&crie, sizeof(crie));
crie.cri_alg = sav->tdb_encalgxform->type;
- crie.cri_klen = _KEYBITS(sav->key_enc);
crie.cri_key = sav->key_enc->key_data;
- if (sav->alg_enc == SADB_X_EALG_AESGCM16)
- arc4rand(crie.cri_iv, sav->ivlen, 0);
-
- /* XXX Rounds ? */
+ crie.cri_klen = _KEYBITS(sav->key_enc) - SAV_ISCTRORGCM(sav) * 32;
if (sav->tdb_authalgxform && sav->tdb_encalgxform) {
/* init both auth & enc */
@@ -291,10 +292,6 @@ esp_zeroize(struct secasvar *sav)
if (sav->key_enc)
bzero(sav->key_enc->key_data, _KEYLEN(sav->key_enc));
- if (sav->iv) {
- free(sav->iv, M_XDATA);
- sav->iv = NULL;
- }
sav->tdb_encalgxform = NULL;
sav->tdb_xform = NULL;
return error;
@@ -310,6 +307,7 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
struct auth_hash *esph;
struct enc_xform *espx;
struct tdb_crypto *tc;
+ uint8_t *ivp;
int plen, alen, hlen;
struct newesp *esp;
struct cryptodesc *crde;
@@ -350,15 +348,13 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
*/
plen = m->m_pkthdr.len - (skip + hlen + alen);
if ((plen & (espx->blocksize - 1)) || (plen <= 0)) {
- if (!espx || sav->alg_enc != SADB_X_EALG_AESGCM16) {
- DPRINTF(("%s: payload of %d octets not a multiple of %d octets,"
- " SA %s/%08lx\n", __func__,
- plen, espx->blocksize, ipsec_address(&sav->sah->saidx.dst,
- buf, sizeof(buf)), (u_long) ntohl(sav->spi)));
- ESPSTAT_INC(esps_badilen);
- m_freem(m);
- return EINVAL;
- }
+ DPRINTF(("%s: payload of %d octets not a multiple of %d octets,"
+ " SA %s/%08lx\n", __func__, plen, espx->blocksize,
+ ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)),
+ (u_long)ntohl(sav->spi)));
+ ESPSTAT_INC(esps_badilen);
+ m_freem(m);
+ return EINVAL;
}
/*
@@ -404,20 +400,13 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
/* Authentication descriptor */
crda->crd_skip = skip;
- if (espx && espx->type == CRYPTO_AES_NIST_GCM_16)
- crda->crd_len = hlen - sav->ivlen;
+ if (SAV_ISGCM(sav))
+ crda->crd_len = 8; /* RFC4106 5, SPI + SN */
else
crda->crd_len = m->m_pkthdr.len - (skip + alen);
crda->crd_inject = m->m_pkthdr.len - alen;
crda->crd_alg = esph->type;
- if (espx && (espx->type == CRYPTO_AES_NIST_GCM_16)) {
- crda->crd_key = sav->key_enc->key_data;
- crda->crd_klen = _KEYBITS(sav->key_enc);
- } else {
- crda->crd_key = sav->key_auth->key_data;
- crda->crd_klen = _KEYBITS(sav->key_auth);
- }
/* Copy the authenticator */
m_copydata(m, m->m_pkthdr.len - alen, alen,
@@ -452,13 +441,26 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen);
crde->crd_inject = skip + hlen - sav->ivlen;
- crde->crd_alg = espx->type;
- crde->crd_key = sav->key_enc->key_data;
- crde->crd_klen = _KEYBITS(sav->key_enc);
- if (espx && (espx->type == CRYPTO_AES_NIST_GCM_16))
+ if (SAV_ISCTRORGCM(sav)) {
+ ivp = &crde->crd_iv[0];
+
+ /* GCM IV Format: RFC4106 4 */
+ /* CTR IV Format: RFC3686 4 */
+ /* Salt is last four bytes of key, RFC4106 8.1 */
+ /* Nonce is last four bytes of key, RFC3686 5.1 */
+ memcpy(ivp, sav->key_enc->key_data +
+ _KEYLEN(sav->key_enc) - 4, 4);
+
+ if (SAV_ISCTR(sav)) {
+ /* Initial block counter is 1, RFC3686 4 */
+ be32enc(&ivp[sav->ivlen + 4], 1);
+ }
+
+ m_copydata(m, skip + hlen - sav->ivlen, sav->ivlen, &ivp[4]);
crde->crd_flags |= CRD_F_IV_EXPLICIT;
+ }
- /* XXX Rounds ? */
+ crde->crd_alg = espx->type;
return (crypto_dispatch(crp));
}
@@ -664,6 +666,8 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
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;
@@ -689,10 +693,9 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
rlen = m->m_pkthdr.len - skip; /* Raw payload length. */
/*
- * NB: The null encoding transform has a blocksize of 4
- * so that headers are properly aligned.
+ * RFC4303 2.4 Requires 4 byte alignment.
*/
- blks = espx->ivsize; /* IV blocksize */
+ blks = MAX(4, espx->blocksize); /* Cipher blocksize */
/* XXX clamp padding length a la KAME??? */
padding = ((blks - ((rlen + 2) % blks)) % blks) + 2;
@@ -816,7 +819,7 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
m_copyback(m, protoff, sizeof(u_int8_t), (u_char *) &prot);
/* Get crypto descriptors. */
- crp = crypto_getreq(esph && espx ? 2 : 1);
+ crp = crypto_getreq(esph != NULL ? 2 : 1);
if (crp == NULL) {
DPRINTF(("%s: failed to acquire crypto descriptors\n",
__func__));
@@ -825,29 +828,9 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
goto bad;
}
- if (espx) {
- crde = crp->crp_desc;
- crda = crde->crd_next;
-
- /* Encryption descriptor. */
- crde->crd_skip = skip + hlen;
- crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen);
- crde->crd_flags = CRD_F_ENCRYPT;
- crde->crd_inject = skip + hlen - sav->ivlen;
-
- /* Encryption operation. */
- crde->crd_alg = espx->type;
- crde->crd_key = sav->key_enc->key_data;
- crde->crd_klen = _KEYBITS(sav->key_enc);
- if (espx->type == CRYPTO_AES_NIST_GCM_16)
- crde->crd_flags |= CRD_F_IV_EXPLICIT;
- /* XXX Rounds ? */
- } else
- crda = crp->crp_desc;
-
/* IPsec-specific opaque crypto info. */
tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto),
- M_XDATA, M_NOWAIT|M_ZERO);
+ M_XDATA, M_NOWAIT|M_ZERO);
if (tc == NULL) {
crypto_freereq(crp);
DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__));
@@ -856,6 +839,40 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
goto bad;
}
+ crde = crp->crp_desc;
+ crda = crde->crd_next;
+
+ /* Encryption descriptor. */
+ crde->crd_skip = skip + hlen;
+ crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen);
+ crde->crd_flags = CRD_F_ENCRYPT;
+ crde->crd_inject = skip + hlen - sav->ivlen;
+
+ /* Encryption operation. */
+ crde->crd_alg = espx->type;
+ if (SAV_ISCTRORGCM(sav)) {
+ ivp = &crde->crd_iv[0];
+
+ /* GCM IV Format: RFC4106 4 */
+ /* CTR IV Format: RFC3686 4 */
+ /* Salt is last four bytes of key, RFC4106 8.1 */
+ /* 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 */
+ be32enc(&ivp[sav->ivlen + 4], 1);
+ }
+
+ m_copyback(m, skip + hlen - sav->ivlen, sav->ivlen, &ivp[4]);
+ crde->crd_flags |= CRD_F_IV_EXPLICIT|CRD_F_IV_PRESENT;
+ }
+
/* Callback parameters */
tc->tc_isr = isr;
KEY_ADDREFSA(sav);
@@ -874,23 +891,13 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
if (esph) {
/* Authentication descriptor. */
+ crda->crd_alg = esph->type;
crda->crd_skip = skip;
- if (espx && espx->type == CRYPTO_AES_NIST_GCM_16)
- crda->crd_len = hlen - sav->ivlen;
+ if (SAV_ISGCM(sav))
+ crda->crd_len = 8; /* RFC4106 5, SPI + SN */
else
crda->crd_len = m->m_pkthdr.len - (skip + alen);
crda->crd_inject = m->m_pkthdr.len - alen;
-
- /* Authentication operation. */
- crda->crd_alg = esph->type;
- if (espx && espx->type == CRYPTO_AES_NIST_GCM_16) {
- crda->crd_key = sav->key_enc->key_data;
- crda->crd_klen = _KEYBITS(sav->key_enc);
- } else {
- crda->crd_key = sav->key_auth->key_data;
- crda->crd_klen = _KEYBITS(sav->key_auth);
- }
-
}
return crypto_dispatch(crp);
@@ -921,7 +928,8 @@ esp_output_cb(struct cryptop *crp)
IPSEC_ASSERT(isr->sp != NULL, ("NULL isr->sp"));
IPSECREQUEST_LOCK(isr);
sav = tc->tc_sav;
- /* With the isr lock released SA pointer can be updated. */
+
+ /* 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",
OpenPOWER on IntegriCloud