Permit sending empty fragments for TLS 1.0.

Due to a weakness in the TLS 1.0 protocol, OpenSSL will periodically
send empty TLS records ("empty fragments").  These TLS records have no
payload (and thus a page count of zero).  m_uiotombuf_nomap() was
returning NULL instead of an empty mbuf, and a few places needed to be
updated to treat an empty TLS record as having a page count of "1" as
0 means "no work to do" (e.g. nothing to encrypt, or nothing to mark
ready via sbready()).

Reviewed by:	gallatin
Sponsored by:	Netflix
Differential Revision:	https://reviews.freebsd.org/D26729
This commit is contained in:
John Baldwin 2020-10-13 17:30:34 +00:00
parent 1775215f88
commit c2a8fd6f05
3 changed files with 38 additions and 9 deletions

View File

@ -1384,7 +1384,9 @@ ktls_seq(struct sockbuf *sb, struct mbuf *m)
* The enq_count argument on return is set to the number of pages of
* payload data for this entire chain that need to be encrypted via SW
* encryption. The returned value should be passed to ktls_enqueue
* when scheduling encryption of this chain of mbufs.
* when scheduling encryption of this chain of mbufs. To handle the
* special case of empty fragments for TLS 1.0 sessions, an empty
* fragment counts as one page.
*/
void
ktls_frame(struct mbuf *top, struct ktls_session *tls, int *enq_cnt,
@ -1400,12 +1402,16 @@ ktls_frame(struct mbuf *top, struct ktls_session *tls, int *enq_cnt,
*enq_cnt = 0;
for (m = top; m != NULL; m = m->m_next) {
/*
* All mbufs in the chain should be non-empty TLS
* records whose payload does not exceed the maximum
* frame length.
* All mbufs in the chain should be TLS records whose
* payload does not exceed the maximum frame length.
*
* Empty TLS records are permitted when using CBC.
*/
KASSERT(m->m_len <= maxlen && m->m_len > 0,
KASSERT(m->m_len <= maxlen &&
(tls->params.cipher_algorithm == CRYPTO_AES_CBC ?
m->m_len >= 0 : m->m_len > 0),
("ktls_frame: m %p len %d\n", m, m->m_len));
/*
* TLS frames require unmapped mbufs to store session
* info.
@ -1496,7 +1502,11 @@ ktls_frame(struct mbuf *top, struct ktls_session *tls, int *enq_cnt,
if (tls->mode == TCP_TLS_MODE_SW) {
m->m_flags |= M_NOTREADY;
m->m_epg_nrdy = m->m_epg_npgs;
*enq_cnt += m->m_epg_npgs;
if (__predict_false(tls_len == 0)) {
/* TLS 1.0 empty fragment. */
*enq_cnt += 1;
} else
*enq_cnt += m->m_epg_npgs;
}
}
}
@ -1961,7 +1971,11 @@ ktls_encrypt(struct mbuf *top)
dst_iov[i].iov_len = len;
}
npages += i;
if (__predict_false(m->m_epg_npgs == 0)) {
/* TLS 1.0 empty fragment. */
npages++;
} else
npages += i;
error = (*tls->sw_encrypt)(tls,
(const struct tls_record_layer *)m->m_epg_hdr,

View File

@ -1655,6 +1655,8 @@ m_uiotombuf_nomap(struct uio *uio, int how, int len, int maxseg, int flags)
int pflags = malloc2vm_flags(how) | VM_ALLOC_NOOBJ | VM_ALLOC_NODUMP |
VM_ALLOC_WIRED;
MPASS((flags & M_PKTHDR) == 0);
/*
* len can be zero or an arbitrary large value bound by
* the total data supplied by the uio.
@ -1667,11 +1669,24 @@ m_uiotombuf_nomap(struct uio *uio, int how, int len, int maxseg, int flags)
if (maxseg == 0)
maxseg = MBUF_PEXT_MAX_PGS * PAGE_SIZE;
/*
* If total is zero, return an empty mbuf. This can occur
* for TLS 1.0 connections which send empty fragments as
* a countermeasure against the known-IV weakness in CBC
* ciphersuites.
*/
if (__predict_false(total == 0)) {
mb = mb_alloc_ext_pgs(how, mb_free_mext_pgs);
if (mb == NULL)
return (NULL);
mb->m_epg_flags = EPG_FLAG_ANON;
return (mb);
}
/*
* Allocate the pages
*/
m = NULL;
MPASS((flags & M_PKTHDR) == 0);
while (total > 0) {
mb = mb_alloc_ext_pgs(how, mb_free_mext_pgs);
if (mb == NULL)

View File

@ -212,7 +212,7 @@ sbready(struct sockbuf *sb, struct mbuf *m0, int count)
while (count > 0) {
KASSERT(m->m_flags & M_NOTREADY,
("%s: m %p !M_NOTREADY", __func__, m));
if ((m->m_flags & M_EXTPG) != 0) {
if ((m->m_flags & M_EXTPG) != 0 && m->m_epg_npgs != 0) {
if (count < m->m_epg_nrdy) {
m->m_epg_nrdy -= count;
count = 0;