Fix GnuTLS DTLS MTU for GnuTLS 3.0.21 and above
authorDavid Woodhouse <David.Woodhouse@intel.com>
Fri, 29 Jun 2012 23:55:06 +0000 (00:55 +0100)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Sat, 30 Jun 2012 00:08:37 +0000 (01:08 +0100)
The fix in 4.01 (commit c218e2ac) was relying on buggy behaviour of
GnuTLS. It shouldn't have been sufficient just to pass it the *data* MTU
plus 13 and rely on the fact that GnuTLS will happily send packets
larger than that. In fixing GnuTLS MTU handling and adding the new
gnutls_dtls_set_data_mtu() function in 3.0.21, I have broken my own
code. And it serves me right.

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
configure.ac
dtls.c
www/changelog.xml

index 980fadd..2312509 100644 (file)
@@ -205,6 +205,8 @@ if test "$with_gnutls" = "yes"; then
     fi
     oldlibs="$LIBS"
     LIBS="$LIBS $GNUTLS_LIBS"
+    AC_CHECK_FUNC(gnutls_dtls_set_data_mtu,
+                [AC_DEFINE(HAVE_GNUTLS_DTLS_SET_DATA_MTU, 1)], [])
     AC_CHECK_FUNC(gnutls_certificate_set_x509_system_trust,
                 [AC_DEFINE(HAVE_GNUTLS_CERTIFICATE_SET_X509_SYSTEM_TRUST, 1)], [])
     AC_CHECK_FUNC(gnutls_pkcs12_simple_parse,
diff --git a/dtls.c b/dtls.c
index 9739b87..5916daa 100644 (file)
--- a/dtls.c
+++ b/dtls.c
@@ -378,8 +378,6 @@ static int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd)
                vpninfo->dtls_attempt_period = 0;
                return -EINVAL;
        }
-       /* +1 for packet header, +13 for DTLS overhead */
-       gnutls_dtls_set_mtu(dtls_ssl, vpninfo->mtu + 14);
        gnutls_transport_set_ptr(dtls_ssl,
                                 (gnutls_transport_ptr_t)(long) dtls_fd);
        gnutls_record_disable_padding(dtls_ssl);
@@ -409,6 +407,29 @@ int dtls_try_handshake(struct openconnect_info *vpninfo)
        int err = gnutls_handshake(vpninfo->new_dtls_ssl);
 
        if (!err) {
+#ifdef HAVE_GNUTLS_DTLS_SET_DATA_MTU
+               /* Make sure GnuTLS's idea of the MTU is sufficient to take
+                  a full VPN MTU (with 1-byte header) in a data record. */
+               err = gnutls_dtls_set_data_mtu(vpninfo->new_dtls_ssl, vpninfo->mtu + 1);
+               if (err) {
+                       vpn_progress(vpninfo, PRG_ERR,
+                                    _("Failed to set DTLS MTU: %s\n"),
+                                    gnutls_strerror(err));
+                       goto error;
+               }
+#else
+               /* If we don't have gnutls_dtls_set_data_mtu() then make sure
+                  we leave enough headroom by adding the worst-case overhead.
+                  We only support AES128-CBC and DES-CBC3-SHA anyway, so
+                  working out the worst case isn't hard. */
+               gnutls_dtls_set_mtu(vpninfo->new_dtls_ssl,
+                                   vpninfo->mtu + 1 /* packet + header */
+                                   + 13 /* DTLS header */
+                                   + 20 /* biggest supported MAC (SHA1) */
+                                   + 16 /* biggest supported IV (AES-128) */
+                                   + 16 /* max padding */);
+#endif
+
                vpn_progress(vpninfo, PRG_INFO, _("Established DTLS connection (using GnuTLS)\n"));
 
                if (vpninfo->dtls_ssl) {
@@ -440,6 +461,8 @@ int dtls_try_handshake(struct openconnect_info *vpninfo)
        vpn_progress(vpninfo, PRG_ERR, _("DTLS handshake failed: %s\n"),
                     gnutls_strerror(err));
 
+       goto error;
+ error:
        /* Kill the new (failed) connection... */
        gnutls_deinit(vpninfo->new_dtls_ssl);
        FD_CLR(vpninfo->new_dtls_fd, &vpninfo->select_rfds);
index 3f1fe23..97c2637 100644 (file)
@@ -17,6 +17,7 @@
 <ul>
    <li><b>OpenConnect HEAD</b>
      <ul>
+       <li>Fix DTLS MTU for GnuTLS 3.0.21 and newer.</li>
        <li>Support more ciphers for OpenSSL encrypted PEM keys, with GnuTLS.</li>
        <li>Fix GnuTLS compatibilty issue with servers that insist on TLSv1.0 <a href="https://bugzilla.redhat.com/show_bug.cgi?id=836558"><i>(RH#836558)</i></a>.</li>
      </ul><br/>