]> granicus.if.org Git - python/commitdiff
merge 3.4 (#22921)
authorBenjamin Peterson <benjamin@python.org>
Sun, 23 Nov 2014 23:06:39 +0000 (17:06 -0600)
committerBenjamin Peterson <benjamin@python.org>
Sun, 23 Nov 2014 23:06:39 +0000 (17:06 -0600)
1  2 
Doc/library/ssl.rst
Lib/ftplib.py
Lib/http/client.py
Lib/imaplib.py
Lib/smtplib.py
Lib/ssl.py
Lib/test/test_ftplib.py
Lib/test/test_imaplib.py
Lib/test/test_poplib.py
Lib/test/test_ssl.py
Modules/_ssl.c

index 6599f88f723d9a8c59b13f303b93e4f461c18f5c,1a0b1dad5cb6a6e2ea6b13ca1c508510a56884fc..3015664739d5ab0730d0200b086258136a862b12
@@@ -1259,22 -1226,13 +1258,23 @@@ to speed up repeated connections from t
     On client connections, the optional parameter *server_hostname* specifies
     the hostname of the service which we are connecting to.  This allows a
     single server to host multiple SSL-based services with distinct certificates,
-    quite similarly to HTTP virtual hosts.  Specifying *server_hostname*
-    will raise a :exc:`ValueError` if the OpenSSL library doesn't have support
-    for it (that is, if :data:`HAS_SNI` is :const:`False`).  Specifying
-    *server_hostname* will also raise a :exc:`ValueError` if *server_side*
-    is true.
+    quite similarly to HTTP virtual hosts. Specifying *server_hostname* will
+    raise a :exc:`ValueError` if *server_side* is true.
+    .. versionchanged:: 3.5
+       Always allow a server_hostname to be passed, even if OpenSSL does not
+       have SNI.
  
 +.. method:: SSLContext.wrap_bio(incoming, outgoing, server_side=False, \
 +                                server_hostname=None)
 +
 +   Create a new :class:`SSLObject` instance by wrapping the BIO objects
 +   *incoming* and *outgoing*. The SSL routines will read input data from the
 +   incoming BIO and write data to the outgoing BIO.
 +
 +   The *server_side* and *server_hostname* parameters have the same meaning as
 +   in :meth:`SSLContext.wrap_socket`.
 +
  .. method:: SSLContext.session_stats()
  
     Get statistics about the SSL sessions created or managed by this context.
diff --cc Lib/ftplib.py
Simple merge
Simple merge
diff --cc Lib/imaplib.py
Simple merge
diff --cc Lib/smtplib.py
Simple merge
diff --cc Lib/ssl.py
Simple merge
Simple merge
index b58707359c677164f5a03ba77158e911d8eb463e,b34e652347fcad871dc5eea97014b3921b4bdf3d..5485a2a7b79f03135251ebc75e4770ef373ee30d
@@@ -19,12 -18,9 +19,9 @@@ try
      import ssl
  except ImportError:
      ssl = None
-     HAS_SNI = False
- else:
-     from ssl import HAS_SNI
  
 -CERTFILE = None
 -CAFILE = None
 +CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "keycert3.pem")
 +CAFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "pycacert.pem")
  
  
  class TestImaplib(unittest.TestCase):
Simple merge
index aa17317782c6791bf79fbcfe132e677f5e022e45,928f5e6a8fae4f394fb9d5f33c443b24111ac83c..06705b208353e0bdb7c1b8aecee13ac1ba4cd143
@@@ -1645,95 -1498,6 +1642,91 @@@ class NetworkedTests(unittest.TestCase)
                  self.assertIs(ss.context, ctx2)
                  self.assertIs(ss._sslobj.context, ctx2)
  
-             if ssl.HAS_SNI:
-                 ctx.check_hostname = True
-                 sslobj = ctx.wrap_bio(incoming, outgoing, False, 'svn.python.org')
-             else:
-                 ctx.check_hostname = False
-                 sslobj = ctx.wrap_bio(incoming, outgoing, False)
 +
 +class NetworkedBIOTests(unittest.TestCase):
 +
 +    def ssl_io_loop(self, sock, incoming, outgoing, func, *args, **kwargs):
 +        # A simple IO loop. Call func(*args) depending on the error we get
 +        # (WANT_READ or WANT_WRITE) move data between the socket and the BIOs.
 +        timeout = kwargs.get('timeout', 10)
 +        count = 0
 +        while True:
 +            errno = None
 +            count += 1
 +            try:
 +                ret = func(*args)
 +            except ssl.SSLError as e:
 +                # Note that we get a spurious -1/SSL_ERROR_SYSCALL for
 +                # non-blocking IO. The SSL_shutdown manpage hints at this.
 +                # It *should* be safe to just ignore SYS_ERROR_SYSCALL because
 +                # with a Memory BIO there's no syscalls (for IO at least).
 +                if e.errno not in (ssl.SSL_ERROR_WANT_READ,
 +                                   ssl.SSL_ERROR_WANT_WRITE,
 +                                   ssl.SSL_ERROR_SYSCALL):
 +                    raise
 +                errno = e.errno
 +            # Get any data from the outgoing BIO irrespective of any error, and
 +            # send it to the socket.
 +            buf = outgoing.read()
 +            sock.sendall(buf)
 +            # If there's no error, we're done. For WANT_READ, we need to get
 +            # data from the socket and put it in the incoming BIO.
 +            if errno is None:
 +                break
 +            elif errno == ssl.SSL_ERROR_WANT_READ:
 +                buf = sock.recv(32768)
 +                if buf:
 +                    incoming.write(buf)
 +                else:
 +                    incoming.write_eof()
 +        if support.verbose:
 +            sys.stdout.write("Needed %d calls to complete %s().\n"
 +                             % (count, func.__name__))
 +        return ret
 +
 +    def test_handshake(self):
 +        with support.transient_internet("svn.python.org"):
 +            sock = socket.socket(socket.AF_INET)
 +            sock.connect(("svn.python.org", 443))
 +            incoming = ssl.MemoryBIO()
 +            outgoing = ssl.MemoryBIO()
 +            ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
 +            ctx.verify_mode = ssl.CERT_REQUIRED
 +            ctx.load_verify_locations(SVN_PYTHON_ORG_ROOT_CERT)
++            ctx.check_hostname = True
++            sslobj = ctx.wrap_bio(incoming, outgoing, False, 'svn.python.org')
 +            self.assertIs(sslobj._sslobj.owner, sslobj)
 +            self.assertIsNone(sslobj.cipher())
 +            self.assertRaises(ValueError, sslobj.getpeercert)
 +            if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES:
 +                self.assertIsNone(sslobj.get_channel_binding('tls-unique'))
 +            self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake)
 +            self.assertTrue(sslobj.cipher())
 +            self.assertTrue(sslobj.getpeercert())
 +            if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES:
 +                self.assertTrue(sslobj.get_channel_binding('tls-unique'))
 +            self.ssl_io_loop(sock, incoming, outgoing, sslobj.unwrap)
 +            self.assertRaises(ssl.SSLError, sslobj.write, b'foo')
 +            sock.close()
 +
 +    def test_read_write_data(self):
 +        with support.transient_internet("svn.python.org"):
 +            sock = socket.socket(socket.AF_INET)
 +            sock.connect(("svn.python.org", 443))
 +            incoming = ssl.MemoryBIO()
 +            outgoing = ssl.MemoryBIO()
 +            ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
 +            ctx.verify_mode = ssl.CERT_NONE
 +            sslobj = ctx.wrap_bio(incoming, outgoing, False)
 +            self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake)
 +            req = b'GET / HTTP/1.0\r\n\r\n'
 +            self.ssl_io_loop(sock, incoming, outgoing, sslobj.write, req)
 +            buf = self.ssl_io_loop(sock, incoming, outgoing, sslobj.read, 1024)
 +            self.assertEqual(buf[:5], b'HTTP/')
 +            self.ssl_io_loop(sock, incoming, outgoing, sslobj.unwrap)
 +            sock.close()
 +
 +
  try:
      import threading
  except ImportError:
diff --cc Modules/_ssl.c
index bf0eeffa2df3b2768b3dad8dd86a29ad18fdc173,90bca98aebed4ead87fdfb2bc1e92ba2f597b44a..de9a5b4e0925539feac0a93e4a21bbf2ede091b9
@@@ -2922,56 -2806,15 +2922,44 @@@ context_wrap_socket(PySSLContext *self
              &sock, &server_side,
              "idna", &hostname))
              return NULL;
- #if !HAVE_SNI
-         PyMem_Free(hostname);
-         PyErr_SetString(PyExc_ValueError, "server_hostname is not supported "
-                         "by your OpenSSL library");
-         return NULL;
- #endif
      }
  
 -    res = (PyObject *) newPySSLSocket(self, sock, server_side,
 -                                      hostname);
 +    res = (PyObject *) newPySSLSocket(self, sock, server_side, hostname,
 +                                      NULL, NULL);
      if (hostname != NULL)
          PyMem_Free(hostname);
      return res;
  }
  
- #if HAVE_SNI
 +static PyObject *
 +context_wrap_bio(PySSLContext *self, PyObject *args, PyObject *kwds)
 +{
 +    char *kwlist[] = {"incoming", "outgoing", "server_side",
 +                      "server_hostname", NULL};
 +    int server_side;
 +    char *hostname = NULL;
 +    PyObject *hostname_obj = Py_None, *res;
 +    PySSLMemoryBIO *incoming, *outgoing;
 +
 +    /* server_hostname is either None (or absent), or to be encoded
 +       using the idna encoding. */
 +    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!i|O:_wrap_bio", kwlist,
 +                                     &PySSLMemoryBIO_Type, &incoming,
 +                                     &PySSLMemoryBIO_Type, &outgoing,
 +                                     &server_side, &hostname_obj))
 +        return NULL;
 +    if (hostname_obj != Py_None) {
- #else
-         PyErr_SetString(PyExc_ValueError, "server_hostname is not supported "
-                         "by your OpenSSL library");
-         return NULL;
- #endif
 +        if (!PyArg_Parse(hostname_obj, "et", "idna", &hostname))
 +            return NULL;
 +    }
 +
 +    res = (PyObject *) newPySSLSocket(self, NULL, server_side, hostname,
 +                                      incoming, outgoing);
 +
 +    PyMem_Free(hostname);
 +    return res;
 +}
 +
  static PyObject *
  session_stats(PySSLContext *self, PyObject *unused)
  {