Re: Bug with imap.gmail.com and getmail TLS validation

Daniel Kahn Gillmor <[email protected]>
Newsgroups gmane.mail.getmail.user
Message-ID <[email protected]>
On Mon 2019-02-11 09:47:09 -0600, Charles Cazabon wrote:
> Daniel Kahn Gillmor <[email protected]> wrote:
>> On Thu 2019-02-07 16:09:37 -0600, Charles Cazabon wrote:
>> > I was able to hack in something to cause the SNI field to be sent with the
>> > request to verify the problem,
>> 
>> Could you publish this particular hack so that other people can try it out?
>
> It's in the test script I posted elsewhere in this thread.

Thanks.  I've just crafted the following (lightly-tested) patch, which
enables SNI by default if the local copy of python's ssl module supports
it, and adds a use_sni boolean config variable which defaults to True.

Please let me know if you think it's missing anything.  I'm considering
applying it to Debian before we run into more gmail users having
problems on that platform.

    --dkg
0001-Send-SNI-on-IMAP-and-POP-connections-by-default.patch (text/x-diff, 12.3 KB)
From bb1d505140a626f5acc4feb9fdf64c404294f722 Mon Sep 17 00:00:00 2001
From: Daniel Kahn Gillmor <[email protected]>
Date: Mon, 11 Feb 2019 14:36:13 -0500
Subject: [PATCH] Send SNI on IMAP and POP connections by default.

This introduces a new "use_sni" configuration option that users can
disable if they need to, but modern TLS connections should probably be
sending SNI.
---
 docs/configuration.html        | 37 ++++++++++++++++++++++++++++++++++
 docs/configuration.txt         | 13 ++++++++++++
 getmailcore/_retrieverbases.py | 35 ++++++++++++++++++++++++++------
 getmailcore/retrievers.py      |  5 +++++
 4 files changed, 84 insertions(+), 6 deletions(-)

diff --git a/docs/configuration.html b/docs/configuration.html
index 10d2424..01e34d6 100644
--- a/docs/configuration.html
+++ b/docs/configuration.html
@@ -790,6 +790,13 @@ openssl s_client -showcerts -connect HOST:PORT &lt; /dev/null 2&gt;/dev/null \
         server's certificate; otherwise, getmail will error out with a hostname
         mismatch.
     </li>
+    <li>
+        ssl_cert_hostname
+        (<a href="#parameter-boolean">boolean</a>)
+        &mdash; Set to <span class="file">False</span> to disable sending the TLS
+        Server Name Indication extension.  Some servers require the client to send
+        SNI to function correctly.  The default is <span class="file">True</span>.
+    </li>
 </ul>
 
 <h4 id="retriever-simplepop3">SimplePOP3Retriever</h4>
@@ -927,6 +934,12 @@ openssl s_client -showcerts -connect HOST:PORT &lt; /dev/null 2&gt;/dev/null \
         &mdash; see <a href="#retriever-ssl-extra">SSL Certificate Validation and Server Parameters</a> 
         for definition
     </li>
+    <li>
+        use_sni
+        (<a href="#parameter-boolean">boolean</a>)
+        &mdash; see <a href="#retriever-ssl-extra">SSL Certificate Validation and Server Parameters</a> 
+        for definition
+    </li>
 </ul>
 
 <h4 id="retriever-brokenpop3ssl">BrokenUIDLPOP3SSLRetriever</h4>
@@ -981,6 +994,12 @@ openssl s_client -showcerts -connect HOST:PORT &lt; /dev/null 2&gt;/dev/null \
         &mdash; see <a href="#retriever-ssl-extra">SSL Certificate Validation and Server Parameters</a> 
         for definition
     </li>
+    <li>
+        use_sni
+        (<a href="#parameter-boolean">boolean</a>)
+        &mdash; see <a href="#retriever-ssl-extra">SSL Certificate Validation and Server Parameters</a> 
+        for definition
+    </li>
 </ul>
 
 <h4 id="retriever-simpleimapssl">SimpleIMAPSSLRetriever</h4>
@@ -1042,6 +1061,12 @@ openssl s_client -showcerts -connect HOST:PORT &lt; /dev/null 2&gt;/dev/null \
         &mdash; see <a href="#retriever-ssl-extra">SSL Certificate Validation and Server Parameters</a> 
         for definition
     </li>
+    <li>
+        use_sni
+        (<a href="#parameter-boolean">boolean</a>)
+        &mdash; see <a href="#retriever-ssl-extra">SSL Certificate Validation and Server Parameters</a> 
+        for definition
+    </li>
 </ul>
 
 <h4 id="retriever-multidroppop3">MultidropPOP3Retriever</h4>
@@ -1152,6 +1177,12 @@ envelope_recipient = delivered-to:1
         &mdash; see <a href="#retriever-ssl-extra">SSL Certificate Validation and Server Parameters</a> 
         for definition
     </li>
+    <li>
+        use_sni
+        (<a href="#parameter-boolean">boolean</a>)
+        &mdash; see <a href="#retriever-ssl-extra">SSL Certificate Validation and Server Parameters</a> 
+        for definition
+    </li>
 </ul>
 
 <h4 id="retriever-multidropsdps">MultidropSDPSRetriever</h4>
@@ -1285,6 +1316,12 @@ envelope_recipient = delivered-to:1
         &mdash; see <a href="#retriever-ssl-extra">SSL Certificate Validation and Server Parameters</a> 
         for definition
     </li>
+    <li>
+        use_sni
+        (<a href="#parameter-boolean">boolean</a>)
+        &mdash; see <a href="#retriever-ssl-extra">SSL Certificate Validation and Server Parameters</a> 
+        for definition
+    </li>
 </ul>
 
 <h3 id="retriever-examples">Retriever examples</h3>
diff --git a/docs/configuration.txt b/docs/configuration.txt
index 43c588b..33e7a17 100644
--- a/docs/configuration.txt
+++ b/docs/configuration.txt
@@ -515,6 +515,9 @@ openssl s_client -showcerts -connect HOST:PORT < /dev/null 2>/dev/null \
        connect to the server is known not to match the hostname in the
        server's certificate; otherwise, getmail will error out with a
        hostname mismatch.
+     * use_sni (boolean) - Set to False to disable sending the TLS
+       Server Name Indication extension.  Some servers require the
+       client to send SNI to function correctly.  The default is True.
 
 SimplePOP3Retriever
 
@@ -577,6 +580,8 @@ SimplePOP3SSLRetriever
        Parameters for definition
      * ssl_fingerprints (tuple of quoted strings) — see SSL Certificate
        Validation and Server Parameters for definition
+     * use_sni (boolean) — see SSL Certificate Validation and Server
+       Parameters for definition
 
 BrokenUIDLPOP3SSLRetriever
 
@@ -593,6 +598,8 @@ BrokenUIDLPOP3SSLRetriever
        Parameters for definition
      * ssl_fingerprints (tuple of quoted strings) — see SSL Certificate
        Validation and Server Parameters for definition
+     * use_sni (boolean) — see SSL Certificate Validation and Server
+       Parameters for definition
 
 SimpleIMAPSSLRetriever
 
@@ -611,6 +618,8 @@ SimpleIMAPSSLRetriever
        Parameters for definition
      * ssl_fingerprints (tuple of quoted strings) — see SSL Certificate
        Validation and Server Parameters for definition
+     * use_sni (boolean) — see SSL Certificate Validation and Server
+       Parameters for definition
 
 MultidropPOP3Retriever
 
@@ -647,6 +656,8 @@ MultidropPOP3SSLRetriever
        Parameters for definition
      * ssl_fingerprints (tuple of quoted strings) — see SSL Certificate
        Validation and Server Parameters for definition
+     * use_sni (boolean) — see SSL Certificate Validation and Server
+       Parameters for definition
 
 MultidropSDPSRetriever
 
@@ -690,6 +701,8 @@ MultidropIMAPSSLRetriever
        Parameters for definition
      * ssl_fingerprints (tuple of quoted strings) — see SSL Certificate
        Validation and Server Parameters for definition
+     * use_sni (boolean) — see SSL Certificate Validation and Server
+       Parameters for definition
 
 Retriever examples
 
diff --git a/getmailcore/_retrieverbases.py b/getmailcore/_retrieverbases.py
index 67f1367..56c5d98 100755
--- a/getmailcore/_retrieverbases.py
+++ b/getmailcore/_retrieverbases.py
@@ -66,6 +66,23 @@ except ImportError:
 
 # If we have an ssl module:
 if ssl:
+    if getattr(ssl, 'HAS_SNI', False):
+        def _wrap_socket(sock, keyfile=None, certfile=None,
+                         server_side=False, cert_reqs=ssl.CERT_NONE,
+                         ssl_version=ssl.PROTOCOL_TLS, ca_certs=None,
+                         do_handshake_on_connect=True,
+                         suppress_ragged_eofs=True,
+                         ciphers=None, server_hostname=None):
+            return ssl.SSLSocket(sock=sock, keyfile=keyfile, certfile=certfile,
+                                 server_side=server_side, cert_reqs=cert_reqs,
+                                 ssl_version=ssl_version, ca_certs=ca_certs,
+                                 do_handshake_on_connect=do_handshake_on_connect,
+                                 suppress_ragged_eofs=suppress_ragged_eofs,
+                                 ciphers=ciphers, server_hostname=server_hostname)
+    else:
+        def _wrap_socket(sock, **kwargs):
+            return ssl.wrap_socket(sock, **kwargs)
+
     # Is it recent enough to have hostname matching (Python 3.2+)?
     try:
         ssl_match_hostname = ssl.match_hostname
@@ -278,7 +295,7 @@ class POP3_SSL_EXTENDED(poplib.POP3_SSL):
 
     def __init__(self, host, port=POP3_SSL_PORT, keyfile=None,
                  certfile=None, ssl_version=None, ca_certs=None,
-                 ssl_ciphers=None):
+                 ssl_ciphers=None, use_sni=True):
         self.host = host
         self.port = port
         self.keyfile = keyfile
@@ -286,6 +303,7 @@ class POP3_SSL_EXTENDED(poplib.POP3_SSL):
         self.ssl_version = ssl_version
         self.ca_certs = ca_certs
         self.ssl_ciphers = ssl_ciphers
+        self.use_sni = use_sni
 
         self.buffer = ''
         msg = "getaddrinfo returns an empty list"
@@ -312,10 +330,12 @@ class POP3_SSL_EXTENDED(poplib.POP3_SSL):
             extra_args['ca_certs'] = self.ca_certs
         if self.ssl_ciphers:
             extra_args['ciphers'] = self.ssl_ciphers
+        if self.use_sni:
+            extra_args['server_hostname'] = self.host
 
         self.file = self.sock.makefile('rb')
-        self.sslobj = ssl.wrap_socket(self.sock, self.keyfile,
-                                      self.certfile, **extra_args)
+        self.sslobj = _wrap_socket(self.sock, self.keyfile,
+                                   self.certfile, **extra_args)
         self._debugging = 0
         self.welcome = self._getresp()
 
@@ -534,10 +554,11 @@ class IMAP4_SSL_EXTENDED(imaplib.IMAP4_SSL):
     # fingerprints, etc.
     def __init__(self, host='', port=imaplib.IMAP4_SSL_PORT, keyfile=None, 
                  certfile=None, ssl_version=None, ca_certs=None, 
-                 ssl_ciphers=None):
+                 ssl_ciphers=None, use_sni=True):
        self.ssl_version = ssl_version
        self.ca_certs = ca_certs
        self.ssl_ciphers = ssl_ciphers
+       self.use_sni = use_sni
        imaplib.IMAP4_SSL.__init__(self, host, port, keyfile, certfile)
 
     def open(self, host='', port=imaplib.IMAP4_SSL_PORT):
@@ -552,9 +573,11 @@ class IMAP4_SSL_EXTENDED(imaplib.IMAP4_SSL):
            extra_args['ca_certs'] = self.ca_certs
        if self.ssl_ciphers:
            extra_args['ciphers'] = self.ssl_ciphers
+       if self.use_sni:
+            extra_args['server_hostname'] = self.host
 
-       self.sslobj = ssl.wrap_socket(self.sock, self.keyfile, self.certfile, 
-                                     **extra_args)
+       self.sslobj = _wrap_socket(self.sock, self.keyfile, self.certfile, 
+                                  **extra_args)
        self.file = self.sslobj.makefile('rb')
 
 
diff --git a/getmailcore/retrievers.py b/getmailcore/retrievers.py
index ecb83e5..7fca807 100755
--- a/getmailcore/retrievers.py
+++ b/getmailcore/retrievers.py
@@ -106,6 +106,7 @@ class SimplePOP3SSLRetriever(POP3RetrieverBase, POP3SSLinitMixIn):
         ConfString(name='ssl_version', required=False, default=None),
         ConfString(name='ssl_ciphers', required=False, default=None),
         ConfString(name='ssl_cert_hostname', required=False, default=None),
+        ConfString(name='use_sni', required=False, default=True),
     )
     received_from = None
     received_with = 'POP3-SSL'
@@ -214,6 +215,7 @@ class BrokenUIDLPOP3SSLRetriever(BrokenUIDLPOP3RetrieverBase, POP3SSLinitMixIn):
         ConfString(name='ssl_version', required=False, default=None),
         ConfString(name='ssl_ciphers', required=False, default=None),
         ConfString(name='ssl_cert_hostname', required=False, default=None),
+        ConfString(name='use_sni', required=False, default=True),
     )
     received_with = 'POP3-SSL'
 
@@ -287,6 +289,7 @@ class MultidropPOP3SSLRetriever(MultidropPOP3RetrieverBase, POP3SSLinitMixIn):
         ConfString(name='ssl_version', required=False, default=None),
         ConfString(name='ssl_ciphers', required=False, default=None),
         ConfString(name='ssl_cert_hostname', required=False, default=None),
+        ConfString(name='use_sni', required=False, default=True),
     )
     received_from = None
     received_with = 'POP3-SSL'
@@ -440,6 +443,7 @@ class SimpleIMAPSSLRetriever(IMAPRetrieverBase, IMAPSSLinitMixIn):
         ConfBool(name='use_kerberos', required=False, default=False),
         ConfBool(name='use_xoauth2', required=False, default=False),
         ConfString(name='ssl_cert_hostname', required=False, default=None),
+        ConfString(name='use_sni', required=False, default=True),
     )
     received_from = None
     received_with = 'IMAP4-SSL'
@@ -535,6 +539,7 @@ class MultidropIMAPSSLRetriever(MultidropIMAPRetrieverBase, IMAPSSLinitMixIn):
         ConfBool(name='use_xoauth2', required=False, default=False),
         ConfString(name='envelope_recipient'),
         ConfString(name='ssl_cert_hostname', required=False, default=None),
+        ConfString(name='use_sni', required=False, default=True),
     )
     received_from = None
     received_with = 'IMAP4-SSL'
-- 
2.20.1
signature.asc (application/pgp-signature, 227 B)
-----BEGIN PGP SIGNATURE-----

iHUEARYKAB0WIQTJDm02IAobkioVCed2GBllKa5f+AUCXGHYXgAKCRB2GBllKa5f
+G3zAP0dfLAMaW+OvAHHYVWDrWy+k0xB9xUBPkl0y0sdnHpIKwEA8ZalG7qxsyMj
xZQZzDsoUK8hyUQVq2sZdwII3zsSygQ=
=YaMy
-----END PGP SIGNATURE-----
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.