Author: jhb
Date: Tue Oct 13 17:30:34 2020
New Revision: 366674
URL: https://svnweb.freebsd.org/changeset/base/366674

Log:
  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

Modified:
  head/sys/kern/uipc_ktls.c
  head/sys/kern/uipc_mbuf.c
  head/sys/kern/uipc_sockbuf.c

Modified: head/sys/kern/uipc_ktls.c
==============================================================================
--- head/sys/kern/uipc_ktls.c   Tue Oct 13 17:27:37 2020        (r366673)
+++ head/sys/kern/uipc_ktls.c   Tue Oct 13 17:30:34 2020        (r366674)
@@ -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,
        *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,
                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 @@ retry_page:
                        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,

Modified: head/sys/kern/uipc_mbuf.c
==============================================================================
--- head/sys/kern/uipc_mbuf.c   Tue Oct 13 17:27:37 2020        (r366673)
+++ head/sys/kern/uipc_mbuf.c   Tue Oct 13 17:30:34 2020        (r366674)
@@ -1655,6 +1655,8 @@ m_uiotombuf_nomap(struct uio *uio, int how, int len, i
        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.
@@ -1668,10 +1670,23 @@ m_uiotombuf_nomap(struct uio *uio, int how, int len, i
                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)

Modified: head/sys/kern/uipc_sockbuf.c
==============================================================================
--- head/sys/kern/uipc_sockbuf.c        Tue Oct 13 17:27:37 2020        
(r366673)
+++ head/sys/kern/uipc_sockbuf.c        Tue Oct 13 17:30:34 2020        
(r366674)
@@ -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;
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to