diff options
Diffstat (limited to 'sys/netinet/ip_esp.c')
-rw-r--r-- | sys/netinet/ip_esp.c | 975 |
1 files changed, 474 insertions, 501 deletions
diff --git a/sys/netinet/ip_esp.c b/sys/netinet/ip_esp.c index b6bf578d455..57ee4adb935 100644 --- a/sys/netinet/ip_esp.c +++ b/sys/netinet/ip_esp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_esp.c,v 1.32 2000/02/07 06:09:09 itojun Exp $ */ +/* $OpenBSD: ip_esp.c,v 1.33 2000/03/17 10:25:22 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), @@ -73,6 +73,12 @@ #include <net/pfkeyv2.h> #include <net/if_enc.h> +#include <sys/md5k.h> +#include <crypto/sha1.h> +#include <crypto/rmd160.h> +#include <crypto/crypto.h> +#include <crypto/xform.h> + #include "bpfilter.h" #ifdef ENCDEBUG @@ -85,34 +91,9 @@ #define offsetof(s, e) ((int)&((s *)0)->e) #endif -extern struct auth_hash auth_hash_hmac_md5_96; -extern struct auth_hash auth_hash_hmac_sha1_96; -extern struct auth_hash auth_hash_hmac_ripemd_160_96; - -struct auth_hash *esp_hash[] = { - &auth_hash_hmac_md5_96, - &auth_hash_hmac_sha1_96, - &auth_hash_hmac_ripemd_160_96 -}; - -extern struct enc_xform enc_xform_des; -extern struct enc_xform enc_xform_3des; -extern struct enc_xform enc_xform_blf; -extern struct enc_xform enc_xform_cast5; -extern struct enc_xform enc_xform_skipjack; - -struct enc_xform *esp_xform[] = { - &enc_xform_des, - &enc_xform_3des, - &enc_xform_blf, - &enc_xform_cast5, - &enc_xform_skipjack, -}; - /* * esp_attach() is called from the transformation initialization code. */ - int esp_attach() { @@ -122,27 +103,39 @@ esp_attach() /* * esp_init() is called when an SPI is being set up. */ - int esp_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii) { struct enc_xform *txform = NULL; struct auth_hash *thash = NULL; - int i; + struct cryptoini cria, crie; - /* Check whether the encryption algorithm is supported */ - for (i = sizeof(esp_xform) / sizeof(esp_xform[0]) - 1; - i >= 0; i--) - if (ii->ii_encalg == esp_xform[i]->type) - break; - - if (i < 0) + switch (ii->ii_encalg) { - DPRINTF(("esp_init(): unsupported encryption algorithm %d specified\n", ii->ii_encalg)); - return EINVAL; - } + case SADB_EALG_DESCBC: + txform = &enc_xform_des; + break; + + case SADB_EALG_3DESCBC: + txform = &enc_xform_3des; + break; + + case SADB_X_EALG_BLF: + txform = &enc_xform_blf; + break; + + case SADB_X_EALG_CAST: + txform = &enc_xform_cast5; + break; + + case SADB_X_EALG_SKIPJACK: + txform = &enc_xform_skipjack; + break; - txform = esp_xform[i]; + default: + DPRINTF(("esp_init(): unsupported encryption algorithm %d specified\n", ii->ii_encalg)); + return EINVAL; + } if (ii->ii_enckeylen < txform->minkey) { @@ -158,18 +151,24 @@ esp_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii) if (ii->ii_authalg) { - for (i = sizeof(esp_hash) / sizeof(esp_hash[0]) - 1; - i >= 0; i--) - if (ii->ii_authalg == esp_hash[i]->type) - break; - - if (i < 0) + switch (ii->ii_authalg) { - DPRINTF(("esp_init(): unsupported authentication algorithm %d specified\n", ii->ii_authalg)); - return EINVAL; - } + case SADB_AALG_MD5HMAC96: + thash = &auth_hash_hmac_md5_96; + break; + + case SADB_AALG_SHA1HMAC96: + thash = &auth_hash_hmac_sha1_96; + break; - thash = esp_hash[i]; + case SADB_X_AALG_RIPEMD160HMAC96: + thash = &auth_hash_hmac_ripemd_160_96; + break; + + default: + DPRINTF(("esp_init(): unsupported authentication algorithm %d specified\n", ii->ii_authalg)); + return EINVAL; + } if (ii->ii_authkeylen != thash->keysize) { @@ -195,53 +194,62 @@ esp_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii) if (tdbp->tdb_flags & TDBF_HALFIV) tdbp->tdb_ivlen /= 2; - /* Initialize the IV */ - get_random_bytes(tdbp->tdb_iv, tdbp->tdb_ivlen); - /* Save the raw keys */ - tdbp->tdb_amxkeylen = ii->ii_authkeylen; - MALLOC(tdbp->tdb_amxkey, u_int8_t *, tdbp->tdb_amxkeylen, M_XDATA, - M_WAITOK); - bcopy(ii->ii_authkey, tdbp->tdb_amxkey, tdbp->tdb_amxkeylen); + if (tdbp->tdb_authalgxform) + { + tdbp->tdb_amxkeylen = ii->ii_authkeylen; + MALLOC(tdbp->tdb_amxkey, u_int8_t *, tdbp->tdb_amxkeylen, M_XDATA, + M_WAITOK); + bcopy(ii->ii_authkey, tdbp->tdb_amxkey, tdbp->tdb_amxkeylen); + } - tdbp->tdb_emxkeylen = ii->ii_enckeylen; - MALLOC(tdbp->tdb_emxkey, u_int8_t *, tdbp->tdb_emxkeylen, M_XDATA, - M_WAITOK); - bcopy(ii->ii_enckey, tdbp->tdb_emxkey, tdbp->tdb_emxkeylen); + if (tdbp->tdb_encalgxform) + { + tdbp->tdb_emxkeylen = ii->ii_enckeylen; + MALLOC(tdbp->tdb_emxkey, u_int8_t *, tdbp->tdb_emxkeylen, M_XDATA, + M_WAITOK); + bcopy(ii->ii_enckey, tdbp->tdb_emxkey, tdbp->tdb_emxkeylen); + } + + /* Initialize crypto session */ + if (tdbp->tdb_encalgxform) + { + bzero(&crie, sizeof(crie)); + + crie.cri_alg = tdbp->tdb_encalgxform->type; - if (txform->setkey) - txform->setkey(&tdbp->tdb_key, ii->ii_enckey, ii->ii_enckeylen); + if (tdbp->tdb_authalgxform) + crie.cri_next = &cria; + else + crie.cri_next = NULL; + + crie.cri_klen = ii->ii_enckeylen * 8; + crie.cri_key = ii->ii_enckey; + /* XXX Rounds ? */ + } - if (thash) + if (tdbp->tdb_authalgxform) { - /* Precompute the I and O pads of the HMAC */ - for (i = 0; i < ii->ii_authkeylen; i++) - ii->ii_authkey[i] ^= HMAC_IPAD_VAL; - - MALLOC(tdbp->tdb_ictx, u_int8_t *, thash->ctxsize, M_XDATA, M_WAITOK); - bzero(tdbp->tdb_ictx, thash->ctxsize); - thash->Init(tdbp->tdb_ictx); - thash->Update(tdbp->tdb_ictx, ii->ii_authkey, ii->ii_authkeylen); - thash->Update(tdbp->tdb_ictx, hmac_ipad_buffer, - HMAC_BLOCK_LEN - ii->ii_authkeylen); - - for (i = 0; i < ii->ii_authkeylen; i++) - ii->ii_authkey[i] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL); - - MALLOC(tdbp->tdb_octx, u_int8_t *, thash->ctxsize, M_XDATA, M_WAITOK); - bzero(tdbp->tdb_octx, thash->ctxsize); - thash->Init(tdbp->tdb_octx); - thash->Update(tdbp->tdb_octx, ii->ii_authkey, ii->ii_authkeylen); - thash->Update(tdbp->tdb_octx, hmac_opad_buffer, - HMAC_BLOCK_LEN - ii->ii_authkeylen); + bzero(&cria, sizeof(cria)); + + cria.cri_alg = tdbp->tdb_authalgxform->type; + cria.cri_next = NULL; + cria.cri_klen = ii->ii_authkeylen * 8; + cria.cri_key = ii->ii_authkey; } - return 0; + return crypto_newsession(&tdbp->tdb_cryptoid, + (tdbp->tdb_encalgxform ? &crie : &cria)); } +/* + * Paranoia. + */ int esp_zeroize(struct tdb *tdbp) { + int err; + if (tdbp->tdb_amxkey) { bzero(tdbp->tdb_amxkey, tdbp->tdb_amxkeylen); @@ -256,69 +264,54 @@ esp_zeroize(struct tdb *tdbp) tdbp->tdb_emxkey = NULL; } - if (tdbp->tdb_key && tdbp->tdb_encalgxform && - tdbp->tdb_encalgxform->zerokey) - tdbp->tdb_encalgxform->zerokey(&tdbp->tdb_key); - - if (tdbp->tdb_ictx) - { - if (tdbp->tdb_authalgxform) - bzero(tdbp->tdb_ictx, tdbp->tdb_authalgxform->ctxsize); - FREE(tdbp->tdb_ictx, M_XDATA); - tdbp->tdb_ictx = NULL; - } - - if (tdbp->tdb_octx) - { - if (tdbp->tdb_authalgxform) - bzero(tdbp->tdb_octx, tdbp->tdb_authalgxform->ctxsize); - FREE(tdbp->tdb_octx, M_XDATA); - tdbp->tdb_octx = NULL; - } - - return 0; + err = crypto_freesession(tdbp->tdb_cryptoid); + tdbp->tdb_cryptoid = 0; + return err; } #define MAXBUFSIZ (AH_ALEN_MAX > ESP_MAX_IVS ? AH_ALEN_MAX : ESP_MAX_IVS) -struct mbuf * +/* + * ESP input processing, called (eventually) through the protocol switch. + */ +int esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) { struct auth_hash *esph = (struct auth_hash *) tdb->tdb_authalgxform; struct enc_xform *espx = (struct enc_xform *) tdb->tdb_encalgxform; - int oplen, plen, alen, ilen, i, blks, rest, count, off, roff, hlen; - u_char iv[MAXBUFSIZ], niv[MAXBUFSIZ], blk[ESP_MAX_BLKS], *lblk; - u_char *idat, *odat, *ivp, *ivn; - struct mbuf *mi, *mo, *m1; - union authctx ctx; + int plen, alen, hlen; u_int32_t btsx; + struct cryptodesc *crde = NULL, *crda = NULL; + struct cryptop *crp; + /* Determine the ESP header length */ if (tdb->tdb_flags & TDBF_NOREPLAY) hlen = sizeof(u_int32_t) + tdb->tdb_ivlen; /* "old" ESP */ else hlen = 2 * sizeof(u_int32_t) + tdb->tdb_ivlen; /* "new" ESP */ - blks = espx->blocksize; - if (esph) alen = AH_HMAC_HASHLEN; else alen = 0; - /* Skip the IP header, IP options, SPI, IV, and any Replay and Auth Data */ - plen = m->m_pkthdr.len - (skip + hlen + alen); - if ((plen & (blks - 1)) || (plen <= 0)) + if (espx) { - DPRINTF(("esp_input(): payload not a multiple of %d octets, SA %s/%08x\n", blks, ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - espstat.esps_badilen++; - m_freem(m); - return NULL; + /* + * Verify payload length is multiple of encryption algorithm + * block size. + */ + plen = m->m_pkthdr.len - (skip + hlen + alen); + if ((plen & (espx->blocksize - 1)) || (plen <= 0)) + { + DPRINTF(("esp_input(): payload not a multiple of %d octets, SA %s/%08x\n", espx->blocksize, ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + espstat.esps_badilen++; + m_freem(m); + return EINVAL; + } } - /* Auth covers SPI + SN + IV */ - oplen = plen + hlen; - /* Replay window checking, if appropriate */ if ((tdb->tdb_wnd > 0) && (!(tdb->tdb_flags & TDBF_NOREPLAY))) { @@ -336,20 +329,20 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) DPRINTF(("esp_input(): replay counter wrapped for SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); espstat.esps_wrap++; m_freem(m); - return NULL; + return EACCES; case 2: case 3: DPRINTF(("esp_input(): duplicate packet received in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); espstat.esps_replay++; m_freem(m); - return NULL; + return EACCES; default: DPRINTF(("esp_input(): bogus value from checkreplaywindow32() in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); espstat.esps_replay++; m_freem(m); - return NULL; + return EACCES; } } @@ -364,10 +357,10 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); tdb_delete(tdb, 0, TDBEXP_TIMEOUT); m_freem(m); - return NULL; + return ENXIO; } - /* Notify on expiration */ + /* Notify on soft expiration */ if ((tdb->tdb_flags & TDBF_SOFT_BYTES) && (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) { @@ -375,114 +368,191 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */ } - /* - * Skip forward to the beginning of the ESP header. If we run out - * of mbufs in the process, the check inside the following while() - * loop will catch it. - */ - for (mo = m, i = 0; mo && i + mo->m_len <= skip; mo = mo->m_next) - i += mo->m_len; - - off = skip - i; - - /* Preserve these for later processing */ - roff = off; - m1 = mo; + /* Get crypto descriptors */ + crp = crypto_getreq(esph && espx ? 2 : 1); + if (crp == NULL) + { + m_freem(m); + DPRINTF(("esp_input(): failed to acquire crypto descriptors\n")); + espstat.esps_crypto++; + return ENOBUFS; + } - /* Verify the authenticator, if applicable */ if (esph) { - bcopy(tdb->tdb_ictx, &ctx, esph->ctxsize); + crda = crp->crp_desc; + crde = crda->crd_next; - /* Copy the authentication data */ - m_copydata(m, m->m_pkthdr.len - alen, alen, iv); + /* Authentication descriptor */ + crda->crd_skip = skip; + crda->crd_len = m->m_pkthdr.len - (skip + alen); + crda->crd_inject = m->m_pkthdr.len - alen; - /* Compute authenticator over the mbuf chain */ - while (oplen > 0) - { - if (mo == NULL) - { - DPRINTF(("esp_input(): bad mbuf chain, SA %s/%08x\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - espstat.esps_hdrops++; - m_freem(m); - return NULL; - } + crda->crd_alg = esph->type; + crda->crd_key = tdb->tdb_amxkey; + crda->crd_klen = tdb->tdb_amxkeylen * 8; + } + else + crde = crp->crp_desc; - count = min(mo->m_len - off, oplen); - esph->Update(&ctx, mtod(mo, unsigned char *) + off, count); - oplen -= count; - off = 0; - mo = mo->m_next; - } + tdb->tdb_ref++; - esph->Final(niv, &ctx); - bcopy(tdb->tdb_octx, &ctx, esph->ctxsize); - esph->Update(&ctx, niv, esph->hashsize); - esph->Final(niv, &ctx); + /* Crypto operation descriptor */ + crp->crp_ilen = m->m_pkthdr.len; /* Total input length */ + crp->crp_flags = CRYPTO_F_IMBUF; + crp->crp_buf = (caddr_t) m; + crp->crp_callback = (int (*) (struct cryptop *)) esp_input_cb; + crp->crp_sid = tdb->tdb_cryptoid; - /* Verify */ - if (bcmp(niv, iv, AH_HMAC_HASHLEN)) - { - DPRINTF(("esp_input(): authentication failed for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - espstat.esps_badauth++; - m_freem(m); - return NULL; - } + /* These are passed as-is to the callback */ + crp->crp_opaque1 = (caddr_t) tdb; + crp->crp_opaque2 = (caddr_t) skip; + crp->crp_opaque3 = (caddr_t) protoff; + + /* Decryption descriptor */ + if (espx) + { + crde->crd_skip = skip + hlen; + crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen); + crde->crd_inject = skip + hlen - tdb->tdb_ivlen; + if (tdb->tdb_flags & TDBF_HALFIV) + crde->crd_flags |= CRD_F_HALFIV; + + crde->crd_alg = espx->type; + crde->crd_key = tdb->tdb_emxkey; + crde->crd_klen = tdb->tdb_emxkeylen * 8; + /* XXX Rounds ? */ } - oplen = plen; + MALLOC(crp->crp_opaque4, caddr_t, alen, M_XDATA, M_DONTWAIT); + if (crp->crp_opaque4 == 0) + { + m_freem(m); + crypto_freereq(crp); + DPRINTF(("esp_input(): failed to allocate auth array\n")); + espstat.esps_crypto++; + return ENOBUFS; + } - /* Find beginning of encrypted data (actually, the IV) */ - mi = m1; - ilen = mi->m_len - roff - sizeof(u_int32_t); - if (!(tdb->tdb_flags & TDBF_NOREPLAY)) - ilen -= sizeof(u_int32_t); - while (ilen <= 0) + /* Copy the authenticator */ + m_copydata(m, m->m_pkthdr.len - alen, alen, crp->crp_opaque4); + + return crypto_dispatch(crp); +} + +/* + * ESP input callback, called directly by the crypto driver. + */ +int +esp_input_cb(void *op) +{ + u_int8_t lastthree[3], aalg[AH_HMAC_HASHLEN]; + int hlen, roff, skip, protoff, error; + struct mbuf *m1, *mo, *m; + struct cryptodesc *crd; + struct auth_hash *esph; + struct enc_xform *espx; + struct cryptop *crp; + struct tdb *tdb; + + crp = (struct cryptop *) op; + crd = crp->crp_desc; + tdb = (struct tdb *) crp->crp_opaque1; + esph = (struct auth_hash *) tdb->tdb_authalgxform; + espx = (struct enc_xform *) tdb->tdb_encalgxform; + skip = (int) crp->crp_opaque2; + protoff = (int) crp->crp_opaque3; + m = (struct mbuf *) crp->crp_buf; + + tdb->tdb_ref--; + + /* Check for crypto errors */ + if (crp->crp_etype) { - mi = mi->m_next; - if (mi == NULL) + /* Reset the session ID */ + if (tdb->tdb_cryptoid != 0) + tdb->tdb_cryptoid = crp->crp_sid; + + if (crp->crp_etype == EAGAIN) { - DPRINTF(("esp_input(): bad mbuf chain, SA %s/%08x\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - espstat.esps_hdrops++; - m_freem(m); - return NULL; + tdb->tdb_ref++; + return crypto_dispatch(crp); } - ilen += mi->m_len; + espstat.esps_noxform++; + DPRINTF(("esp_input_cb(): crypto error %d\n", crp->crp_etype)); + error = crp->crp_etype; + goto baddone; } - idat = mtod(mi, unsigned char *) + (mi->m_len - ilen); - m_copydata(mi, mi->m_len - ilen, tdb->tdb_ivlen, iv); + /* Shouldn't happen... */ + if (!m) + { + espstat.esps_crypto++; + DPRINTF(("esp_input_cb(): bogus returned buffer from crypto\n")); + error = EINVAL; + goto baddone; + } - /* If we're doing half-IV, generate full IV */ - if (tdb->tdb_flags & TDBF_HALFIV) + /* + * Check that the TDB is still valid -- not really an error, but + * we need to handle it as such. It may happen if the TDB expired + * or was deleted while there was a pending request in the crypto + * queue. + */ + if (tdb->tdb_flags & TDBF_INVALID) { - for (i = 0; i < tdb->tdb_ivlen; i++) - iv[tdb->tdb_ivlen + i] = ~iv[i]; + espstat.esps_invalid++; + tdb_delete(tdb, 0, 0); + error = ENXIO; + DPRINTF(("esp_input_cb(): TDB expired while processing crypto\n")); + goto baddone; } - /* Now skip over the IV */ - ilen -= tdb->tdb_ivlen; - while (ilen <= 0) + /* If authentication was performed, check now */ + if (esph) { - mi = mi->m_next; - if (mi == NULL) + /* Copy the authenticator from the packet */ + m_copydata(m, m->m_pkthdr.len - esph->authsize, + esph->authsize, aalg); + + /* Verify authenticator */ + if (bcmp(crp->crp_opaque4, aalg, esph->authsize)) { - DPRINTF(("esp_input(): bad mbuf chain, SA %s/%08x\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - espstat.esps_hdrops++; - m_freem(m); - return NULL; + DPRINTF(("esp_input_cb(): authentication failed for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + espstat.esps_badauth++; + error = EACCES; + goto baddone; } - ilen += mi->m_len; + /* Remove trailing authenticator */ + m_adj(m, -(esph->authsize)); + + /* We have to manually free this */ + FREE(crp->crp_opaque4, M_XDATA); } - /* - * Remove the ESP header and IV from the mbuf. - */ + /* Release the crypto descriptors */ + crypto_freereq(crp); + + /* Determine the ESP header length */ + if (tdb->tdb_flags & TDBF_NOREPLAY) + hlen = sizeof(u_int32_t) + tdb->tdb_ivlen; /* "old" ESP */ + else + hlen = 2 * sizeof(u_int32_t) + tdb->tdb_ivlen; /* "new" ESP */ + + /* Find beginning of ESP header */ + m1 = m_getptr(m, skip, &roff); + if (m1 == NULL) + { + DPRINTF(("esp_input_cb(): bad mbuf chain, SA %s/%08x\n", + ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + espstat.esps_hdrops++; + m_freem(m); + return EINVAL; + } + + /* Remove the ESP header and IV from the mbuf. */ if (roff == 0) { /* The ESP header was conveniently at the beginning of the mbuf */ @@ -494,8 +564,8 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) if (roff + hlen >= m1->m_len) { /* - * Part or all of the ESP header is at the end of this mbuf, so first - * let's remove the remainder of the ESP header from the + * Part or all of the ESP header is at the end of this mbuf, so + * first let's remove the remainder of the ESP header from the * beginning of the remainder of the mbuf chain, if any. */ if (roff + hlen > m1->m_len) @@ -532,166 +602,67 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) m->m_pkthdr.len -= hlen; } - /* Point to the encrypted data */ - idat = mtod(mi, unsigned char *) + (mi->m_len - ilen); - - /* - * At this point: - * plen is # of encapsulated payload octets - * ilen is # of octets left in this mbuf - * idat is first encapsulated payload octed in this mbuf - * same for olen and odat - * ivp points to the IV, ivn buffers the next IV. - * mi points to the first mbuf - * - * From now on until the end of the mbuf chain: - * . move the next eight octets of the chain into ivn - * . decrypt idat and xor with ivp - * . swap ivp and ivn. - * . repeat - */ + /* Save the last three bytes of decrypted data */ + m_copydata(m, m->m_pkthdr.len - 3, 3, lastthree); - ivp = iv; - ivn = niv; - rest = ilen % blks; - while (plen > 0) /* while not done */ + /* Verify pad length */ + if (lastthree[1] + 2 > m->m_pkthdr.len - skip - hlen) { - if (ilen < blks) - { - if (rest) - { - bcopy(idat, blk, rest); - odat = idat; - } - - do { - mi = (mo = mi)->m_next; - if (mi == NULL) - { - DPRINTF(("esp_input(): bad mbuf chain, SA %s/%08x\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - espstat.esps_hdrops++; - m_freem(m); - return NULL; - } - } while (mi->m_len == 0); - - if (mi->m_len < blks - rest) - { - if ((mi = m_pullup(mi, blks - rest)) == NULL) - { - DPRINTF(("esp_input(): m_pullup() failed, SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - m_freem(m); - espstat.esps_hdrops++; - return NULL; - } - /* - * m_pullup was not called at the beginning of the chain - * but might return a new mbuf, link it into the chain. - */ - mo->m_next = mi; - } - - ilen = mi->m_len; - idat = mtod(mi, u_char *); - - if (rest) - { - bcopy(idat, blk + rest, blks - rest); - bcopy(blk, ivn, blks); - - espx->decrypt(tdb, blk); - - for (i = 0; i < blks; i++) - blk[i] ^= ivp[i]; - - ivp = ivn; - ivn = (ivp == iv) ? niv : iv; - - bcopy(blk, odat, rest); - bcopy(blk + rest, idat, blks - rest); - - lblk = blk; /* last block touched */ - - idat += blks - rest; - ilen -= blks - rest; - plen -= blks; - } - - rest = ilen % blks; - } - - while (ilen >= blks && plen > 0) - { - bcopy(idat, ivn, blks); - - espx->decrypt(tdb, idat); - - for (i = 0; i < blks; i++) - idat[i] ^= ivp[i]; - - ivp = ivn; - ivn = (ivp == iv) ? niv : iv; - - lblk = idat; /* last block touched */ - idat += blks; - - ilen -= blks; - plen -= blks; - } - } - - /* Save last block (end of padding), if it was in-place decrypted */ - if (lblk != blk) - bcopy(lblk, blk, blks); - - /* - * Now, the entire chain has been decrypted. As a side effect, - * blk[blks - 1] contains the next protocol, and blk[blks - 2] contains - * the amount of padding the original chain had. Chop off the - * appropriate parts of the chain, and return. - */ - - if (blk[blks - 2] + 2 + alen > m->m_pkthdr.len - skip - hlen) - { - DPRINTF(("esp_input(): invalid padding length %d for packet in SA %s/%08x\n", blk[blks - 2], ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + DPRINTF(("esp_input_cb(): invalid padding length %d for packet in SA %s/%08x\n", lastthree[2], ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); espstat.esps_badilen++; m_freem(m); - return NULL; + return EINVAL; } - /* Verify correct decryption by checking the last padding bytes. */ + /* Verify correct decryption by checking the last padding bytes */ if (!(tdb->tdb_flags & TDBF_RANDOMPADDING)) { - if ((blk[blks - 2] != blk[blks - 3]) && (blk[blks - 2] != 0)) + if ((lastthree[1] != lastthree[0]) && (lastthree[1] != 0)) { DPRINTF(("esp_input(): decryption failed for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); espstat.esps_badenc++; m_freem(m); - return NULL; + return EINVAL; } } - /* Trim the mbuf chain to remove the trailing authenticator */ - m_adj(m, - blk[blks - 2] - 2 - alen); + /* Trim the mbuf chain to remove the trailing authenticator and padding */ + m_adj(m, -(lastthree[1] + 2)); /* Restore the Next Protocol field */ - m_copyback(m, protoff, 1, &blk[blks - 1]); + m_copyback(m, protoff, sizeof(u_int8_t), lastthree + 2); + + /* Back to generic IPsec input processing */ + return ipsec_common_input_cb(m, tdb, skip, protoff); + + baddone: + if (m) + m_freem(m); - return m; + /* We have to manually free this */ + if (crp && crp->crp_opaque4) + FREE(crp->crp_opaque4, M_XDATA); + + crypto_freereq(crp); + + return error; } +/* + * ESP output routine, called by ipsp_process_packet(). + */ int esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, - int protoff) + int protoff) { struct enc_xform *espx = (struct enc_xform *) tdb->tdb_encalgxform; struct auth_hash *esph = (struct auth_hash *) tdb->tdb_authalgxform; - u_char iv[ESP_MAX_IVS], blk[ESP_MAX_BLKS], auth[AH_ALEN_MAX]; - int i, ilen, hlen, rlen, plen, padding, rest, blks, alen; + int ilen, hlen, rlen, plen, padding, blks, alen; struct mbuf *mi, *mo = (struct mbuf *) NULL; - u_char *pad, *idat, *odat, *ivp; - union authctx ctx; + unsigned char *pad; + + struct cryptodesc *crde = NULL, *crda = NULL; + struct cryptop *crp; #if NBPFILTER > 0 { @@ -727,8 +698,12 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, else hlen = 2 * sizeof(u_int32_t) + tdb->tdb_ivlen; - blks = espx->blocksize; rlen = m->m_pkthdr.len - skip; /* Raw payload length */ + if (espx) + blks = espx->blocksize; + else + blks = 4; /* If no encryption is used, we have to be 4-byte aligned */ + padding = ((blks - ((rlen + 2) % blks)) % blks) + 2; plen = rlen + padding; /* Padded payload length */ @@ -743,39 +718,50 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, if ((!(tdb->tdb_flags & TDBF_NOREPLAY)) && (tdb->tdb_rpl == 0) && (tdb->tdb_wnd > 0)) { - DPRINTF(("esp_output(): SA %s/%0x8 should have expired\n", + DPRINTF(("esp_output(): SA %s/%08x should have expired\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); m_freem(m); espstat.esps_wrap++; - return ENOBUFS; + return EACCES; } + switch (tdb->tdb_dst.sa.sa_family) + { #ifdef INET - /* Check for IPv6 maximum packet size violations */ - if (tdb->tdb_dst.sa.sa_family == AF_INET) - if (skip + hlen + rlen + padding + alen > IP_MAXPACKET) - { - DPRINTF(("esp_output(): packet in SA %s/%0x8 got too big\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - m_freem(m); - espstat.esps_toobig++; - return EMSGSIZE; - } + case AF_INET: + /* Check for IP maximum packet size violations */ + if (skip + hlen + rlen + padding + alen > IP_MAXPACKET) + { + DPRINTF(("esp_output(): packet in SA %s/%08x got too big\n", + ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + m_freem(m); + espstat.esps_toobig++; + return EMSGSIZE; + } + break; #endif /* INET */ #ifdef INET6 - /* Check for IPv6 maximum packet size violations */ - if (tdb->tdb_dst.sa.sa_family == AF_INET6) - if (skip + hlen + rlen + padding + alen > IPV6_MAXPACKET) - { - DPRINTF(("esp_output(): packet in SA %s/%0x8 got too big\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - m_freem(m); - espstat.esps_toobig++; - return EMSGSIZE; - } + case AF_INET6: + /* Check for IPv6 maximum packet size violations */ + if (skip + hlen + rlen + padding + alen > IPV6_MAXPACKET) + { + DPRINTF(("esp_output(): packet in SA %s/%08x got too big\n", + ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + m_freem(m); + espstat.esps_toobig++; + return EMSGSIZE; + } + break; #endif /* INET6 */ + default: + DPRINTF(("esp_output(): unknown/unsupported protocol family %d, SA %s/%08x\n", tdb->tdb_dst.sa.sa_family, ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + m_freem(m); + espstat.esps_nopf++; + return EPFNOSUPPORT; + } + /* Update the counters */ tdb->tdb_cur_bytes += m->m_pkthdr.len - skip; espstat.esps_obytes += m->m_pkthdr.len - skip; @@ -800,7 +786,7 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, /* * Loop through mbuf chain; if we find an M_EXT mbuf with - * more than one reference, replace the rest of the chain. + * more than one reference, replace the rest of the chain. */ mi = m; while (mi != NULL && @@ -833,37 +819,29 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, } /* Inject ESP header */ - mo = m_inject(m, skip, hlen, M_WAITOK); + mo = m_inject(m, skip, hlen, M_DONTWAIT); if (mo == NULL) { - DPRINTF(("esp_output(): failed to inject ESP header for SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + DPRINTF(("esp_output(): failed to inject ESP header for SA %s/%08x\n", + ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); m_freem(m); espstat.esps_wrap++; return ENOBUFS; } /* Initialize ESP header */ - m_copyback(mo, 0, sizeof(u_int32_t), (caddr_t) &tdb->tdb_spi); + bcopy((caddr_t) &tdb->tdb_spi, mtod(mo, caddr_t), sizeof(u_int32_t)); if (!(tdb->tdb_flags & TDBF_NOREPLAY)) { u_int32_t replay = htonl(tdb->tdb_rpl++); - m_copyback(mo, sizeof(u_int32_t), sizeof(u_int32_t), - (caddr_t) &replay); + bcopy((caddr_t) &replay, mtod(mo, caddr_t) + sizeof(u_int32_t), + sizeof(u_int32_t)); } - /* Initialize IV (cook the half-IV if applicable) */ - bcopy(tdb->tdb_iv, iv, tdb->tdb_ivlen); - if (tdb->tdb_flags & TDBF_HALFIV) - { - for (i = 0; i < tdb->tdb_ivlen; i++) - iv[i + tdb->tdb_ivlen] = ~iv[i]; - } - - /* Copy IV in ESP header */ - m_copyback(mo, hlen - tdb->tdb_ivlen, tdb->tdb_ivlen, - (caddr_t) &tdb->tdb_iv); - - /* Add padding */ + /* + * Add padding -- better to do it ourselves than use the crypto engine, + * although if/when we support compression, we'd have to do that. + */ pad = (u_char *) m_pad(m, padding + alen, tdb->tdb_flags & TDBF_RANDOMPADDING); if (pad == NULL) @@ -882,145 +860,142 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, /* Fix padding length and Next Protocol in padding itself */ pad[padding - 2] = padding - 2; - m_copydata(m, protoff, 1, &pad[padding - 1]); + m_copydata(m, protoff, sizeof(u_int8_t), pad + padding - 1); /* Fix Next Protocol in IPv4/IPv6 header */ ilen = IPPROTO_ESP; m_copyback(m, protoff, sizeof(u_int8_t), (u_char *) &ilen); - mi = mo; - - /* If it's just the ESP header, just skip to the next mbuf */ - if (mi->m_len == hlen) + /* Get crypto descriptors */ + crp = crypto_getreq(esph && espx ? 2 : 0); + if (crp == NULL) { - mi = mi->m_next; - ilen = mi->m_len; - idat = mtod(mi, u_char *); - } - else - { /* There's data at the end of this mbuf, skip over ESP header */ - ilen = mi->m_len - hlen; - idat = mtod(mi, u_char *) + hlen; + m_freem(m); + DPRINTF(("esp_output(): failed to acquire crypto descriptors\n")); + espstat.esps_crypto++; + return ENOBUFS; } - /* Authenticate the ESP header if applicable */ - if (esph) + if (espx) { - bcopy(tdb->tdb_ictx, &ctx, esph->ctxsize); - esph->Update(&ctx, mtod(mo, unsigned char *), hlen); + 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; + if (tdb->tdb_flags & TDBF_HALFIV) + crde->crd_flags |= CRD_F_HALFIV; + crde->crd_inject = skip + hlen - tdb->tdb_ivlen; + + /* Encryption operation */ + crde->crd_alg = espx->type; + crde->crd_key = tdb->tdb_emxkey; + crde->crd_klen = tdb->tdb_emxkeylen * 8; + /* XXX Rounds ? */ } + else + crda = crp->crp_desc; - /* Encrypt the payload */ - ivp = iv; - rest = ilen % blks; - while (plen > 0) /* while not done */ - { - if (ilen < blks) - { - if (rest) - { - if (ivp == blk) - { - bcopy(blk, iv, blks); - ivp = iv; - } - - bcopy(idat, blk, rest); - odat = idat; - } + tdb->tdb_ref++; - do { - mi = (mo = mi)->m_next; - if (mi == NULL) - { - DPRINTF(("esp_output(): bad mbuf chain, SA %s/%08x\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - espstat.esps_hdrops++; - m_freem(m); - return EINVAL; - } - } while (mi->m_len == 0); - - if (mi->m_len < blks - rest) - { - if ((mi = m_pullup(mi, blks - rest)) == NULL) - { - DPRINTF(("esp_output(): m_pullup() failed, SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - m_freem(m); - espstat.esps_hdrops++; - return ENOBUFS; - } - /* - * m_pullup was not called at the beginning of the chain - * but might return a new mbuf, link it into the chain. - */ - mo->m_next = mi; - } - - ilen = mi->m_len; - idat = mtod(mi, u_char *); + /* Crypto operation descriptor */ + crp->crp_ilen = m->m_pkthdr.len; /* Total input length */ + crp->crp_flags = CRYPTO_F_IMBUF; + crp->crp_buf = (caddr_t) m; + crp->crp_callback = (int (*) (struct cryptop *)) esp_output_cb; + crp->crp_opaque1 = (caddr_t) tdb; + crp->crp_sid = tdb->tdb_cryptoid; - if (rest) - { - bcopy(idat, blk + rest, blks - rest); - - for (i = 0; i < blks; i++) - blk[i] ^= ivp[i]; + if (esph) + { + /* Authentication descriptor */ + crda->crd_skip = skip; + crda->crd_len = m->m_pkthdr.len - (skip + alen); + crda->crd_inject = m->m_pkthdr.len - alen; + + /* Authentication operation */ + crda->crd_alg = esph->type; + crda->crd_key = tdb->tdb_amxkey; + crda->crd_klen = tdb->tdb_amxkeylen * 8; + } - espx->encrypt(tdb, blk); + return crypto_dispatch(crp); +} - if (esph) - esph->Update(&ctx, blk, blks); +/* + * ESP output callback, called directly by the crypto driver. + */ +int +esp_output_cb(void *op) +{ + struct cryptop *crp = (struct cryptop *) op; + struct tdb *tdb; + struct mbuf *m; + int error; - ivp = blk; + tdb = (struct tdb *) crp->crp_opaque1; + m = (struct mbuf *) crp->crp_buf; - bcopy(blk, odat, rest); - bcopy(blk + rest, idat, blks - rest); - - idat += blks - rest; - ilen -= blks - rest; - plen -= blks; - } + tdb->tdb_ref--; - rest = ilen % blks; - } + /* Check for crypto errors */ + if (crp->crp_etype) + { + /* Reset session ID */ + if (tdb->tdb_cryptoid != 0) + tdb->tdb_cryptoid = crp->crp_sid; - while (ilen >= blks && plen > 0) + if (crp->crp_etype == EAGAIN) { - for (i = 0; i < blks; i++) - idat[i] ^= ivp[i]; - - espx->encrypt(tdb, idat); - - if (esph) - esph->Update(&ctx, idat, blks); - - ivp = idat; - idat += blks; - - ilen -= blks; - plen -= blks; + tdb->tdb_ref++; + return crypto_dispatch(crp); } + + espstat.esps_noxform++; + DPRINTF(("esp_output_cb(): crypto error %d\n", crp->crp_etype)); + error = crp->crp_etype; + goto baddone; } - /* Put in authentication data */ - if (esph) + /* Shouldn't happen... */ + if (!m) { - esph->Final(auth, &ctx); - bcopy(tdb->tdb_octx, &ctx, esph->ctxsize); - esph->Update(&ctx, auth, esph->hashsize); - esph->Final(auth, &ctx); + espstat.esps_crypto++; + DPRINTF(("esp_output_cb(): bogus returned buffer from crypto\n")); + error = EINVAL; + goto baddone; + } - /* Copy the final authenticator -- cheat and use bcopy() again */ - bcopy(auth, pad + padding, alen); + /* + * Check that the TDB is still valid -- not really an error, but + * we need to handle it as such. It may happen if the TDB expired + * or was deleted while there was a pending request in the crypto + * queue. + */ + if (tdb->tdb_flags & TDBF_INVALID) + { + espstat.esps_invalid++; + tdb_delete(tdb, 0, 0); + error = ENXIO; + DPRINTF(("esp_output_cb(): TDB expired while processing crypto\n")); + goto baddone; } - - /* Save the last encrypted block, to be used as the next IV */ - bcopy(ivp, tdb->tdb_iv, tdb->tdb_ivlen); - *mp = m; + /* Release crypto descriptors */ + crypto_freereq(crp); - return 0; + /* Call the IPsec input callback */ + return ipsp_process_done(m, tdb); + + baddone: + if (m) + m_freem(m); + + crypto_freereq(crp); + + return error; } /* @@ -1057,6 +1032,7 @@ checkreplaywindow32(u_int32_t seq, u_int32_t initial, u_int32_t *lastseq, espstat.esps_wrap++; return 2; } + if ((*bitmap) & (((u_int32_t) 1) << diff)) { espstat.esps_replay++; @@ -1068,13 +1044,10 @@ checkreplaywindow32(u_int32_t seq, u_int32_t initial, u_int32_t *lastseq, } /* - * - * * m_pad(m, n) pads <m> with <n> bytes at the end. The packet header * length is updated, and a pointer to the first byte of the padding * (which is guaranteed to be all in one mbuf) is returned. The third * argument specifies whether we need randompadding or not. - * */ caddr_t |