libtls follows the SNI RFC and errors when clients try use SNI with an
IP address.  Python on the other hand doesn't follow the RFC resulting
in the below mess.

Gate the SSL_set_tlsext_host_name() calls with inet_pton() calls like
libtls does when acting as a client.

import httplib
import ssl

context = ssl._create_unverified_context()

# google works
#conn = httplib.HTTPSConnection('216.58.199.68', 443, context=context)
# www.openbsd.org fails
conn = httplib.HTTPSConnection('129.128.5.194', 443, context=context)

conn.request('GET', '/')
httpsres = conn.getresponse()

Traceback (most recent call last):
  File "openbsd.py", line 11, in <module>
    conn.request('GET', '/')
  File "/usr/local/lib/python2.7/httplib.py", line 1042, in request
    self._send_request(method, url, body, headers)
  File "/usr/local/lib/python2.7/httplib.py", line 1082, in _send_request
    self.endheaders(body)
  File "/usr/local/lib/python2.7/httplib.py", line 1038, in endheaders
    self._send_output(message_body)
  File "/usr/local/lib/python2.7/httplib.py", line 882, in _send_output
    self.send(msg)
  File "/usr/local/lib/python2.7/httplib.py", line 844, in send
    self.connect()
  File "/usr/local/lib/python2.7/httplib.py", line 1263, in connect
    server_hostname=server_hostname)
  File "/usr/local/lib/python2.7/ssl.py", line 363, in wrap_socket
    _context=self)
  File "/usr/local/lib/python2.7/ssl.py", line 611, in __init__
    self.do_handshake()
  File "/usr/local/lib/python2.7/ssl.py", line 840, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: TLSV1_ALERT_INTERNAL_ERROR] tlsv1 alert internal error 
(_ssl.c:661)

Index: Makefile
===================================================================
RCS file: /cvs/ports/lang/python/2.7/Makefile,v
retrieving revision 1.51
diff -u -p -r1.51 Makefile
--- Makefile    4 May 2017 15:20:24 -0000       1.51
+++ Makefile    2 Jun 2017 15:40:40 -0000
@@ -7,7 +7,7 @@
 
 VERSION =              2.7
 PATCHLEVEL =           .13
-REVISION =             1
+REVISION =             2
 SHARED_LIBS =          python2.7 0.0
 VERSION_SPEC =         >=2.7,<2.8
 
Index: patches/patch-Modules__ssl_c
===================================================================
RCS file: patches/patch-Modules__ssl_c
diff -N patches/patch-Modules__ssl_c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ patches/patch-Modules__ssl_c        2 Jun 2017 15:44:00 -0000
@@ -0,0 +1,44 @@
+$OpenBSD$
+
+- Use ALPN with LibreSSL
+- Make SNI use RFC compliant
+
+Index: Modules/_ssl.c
+--- Modules/_ssl.c.orig
++++ Modules/_ssl.c
+@@ -118,7 +118,7 @@ struct py_ssl_library_code {
+ #endif
+ 
+ /* ALPN added in OpenSSL 1.0.2 */
+-#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 
0x1000200fL && !defined(OPENSSL_NO_TLSEXT)
++#if OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined(OPENSSL_NO_TLSEXT)
+ # define HAVE_ALPN
+ #endif
+ 
+@@ -545,6 +545,8 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObjec
+     PySSLSocket *self;
+     SSL_CTX *ctx = sslctx->ctx;
+     long mode;
++    struct in_addr addrbuf;
++    struct in6_addr addrbuf6;
+ 
+     self = PyObject_New(PySSLSocket, &PySSLSocket_Type);
+     if (self == NULL)
+@@ -575,8 +577,15 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObjec
+     SSL_set_mode(self->ssl, mode);
+ 
+ #if HAVE_SNI
+-    if (server_hostname != NULL)
+-        SSL_set_tlsext_host_name(self->ssl, server_hostname);
++    /*
++     * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not
++     * permitted in "HostName".
++     */
++    if (server_hostname != NULL &&
++        inet_pton(AF_INET, server_hostname, &addrbuf) != 1 &&
++        inet_pton(AF_INET6, server_hostname, &addrbuf6) != 1) {
++            SSL_set_tlsext_host_name(self->ssl, server_hostname);
++    }
+ #endif
+ 
+     /* If the socket is in non-blocking mode or timeout mode, set the BIO

Reply via email to