dirmngr: Gracefully handle premature termination of TLS streams. fix-1950
authorJustus Winter <justus@g10code.com>
Wed, 18 Nov 2015 14:06:48 +0000 (15:06 +0100)
committerJustus Winter <justus@g10code.com>
Wed, 18 Nov 2015 14:06:48 +0000 (15:06 +0100)
* dirmngr/http.c (close_tls_session): New function.
(session_unref): Use the new function to close the TLS stream.
(cookie_read): If the stream terminated prematurely, close it and
return a short read.
--
With this patch, I was able to update all the keys in the Debian
keyring over hkps.

Signed-off-by: Justus Winter <justus@g10code.com>
GnuPG-bug-id: 1950

dirmngr/http.c

index e33a594..60dc45f 100644 (file)
@@ -511,6 +511,27 @@ http_register_tls_ca (const char *fname)
 }
 
 
+#ifdef USE_TLS
+/* Free the TLS session associated with SESS, if any.  */
+static void
+close_tls_session (http_session_t sess)
+{
+  if (sess->tls_session)
+    {
+# ifdef HTTP_USE_GNUTLS
+      my_socket_t sock = gnutls_transport_get_ptr (sess->tls_session);
+      my_socket_unref (sock, NULL, NULL);
+      gnutls_deinit (sess->tls_session);
+      if (sess->certcred)
+        gnutls_certificate_free_credentials (sess->certcred);
+# endif /*HTTP_USE_GNUTLS*/
+      xfree (sess->servername);
+      sess->tls_session = NULL;
+    }
+}
+#endif /*USE_TLS*/
+
+
 /* Release a session.  Take care not to release it while it is being
    used by a http context object.  */
 static void
@@ -527,17 +548,7 @@ session_unref (int lnr, http_session_t sess)
     return;
 
 #ifdef USE_TLS
-# ifdef HTTP_USE_GNUTLS
-  if (sess->tls_session)
-    {
-      my_socket_t sock = gnutls_transport_get_ptr (sess->tls_session);
-      my_socket_unref (sock, NULL, NULL);
-      gnutls_deinit (sess->tls_session);
-    }
-  if (sess->certcred)
-    gnutls_certificate_free_credentials (sess->certcred);
-# endif /*HTTP_USE_GNUTLS*/
-  xfree (sess->servername);
+  close_tls_session (sess);
 #endif /*USE_TLS*/
 
   xfree (sess);
@@ -2447,6 +2458,13 @@ cookie_read (void *cookie, void *buffer, size_t size)
             }
           if (nread == GNUTLS_E_REHANDSHAKE)
             goto again; /* A client is allowed to just ignore this request. */
+          if (nread == GNUTLS_E_PREMATURE_TERMINATION)
+            {
+              /* The server terminated the connection.  Close the TLS
+                 session, and indicate EOF using a short read.  */
+              close_tls_session (c->session);
+              return 0;
+            }
           log_info ("TLS network read failed: %s\n", gnutls_strerror (nread));
           gpg_err_set_errno (EIO);
           return -1;