diff -r 63c1fbc4de4b Doc/library/poplib.rst --- a/Doc/library/poplib.rst Mon Nov 25 23:19:58 2013 +0100 +++ b/Doc/library/poplib.rst Tue Nov 26 00:40:45 2013 +0100 @@ -48,11 +48,16 @@ *timeout* works as in the :class:`POP3` constructor. *context* parameter is a :class:`ssl.SSLContext` object which allows bundling SSL configuration options, certificates and private keys into a single (potentially long-lived) - structure. + structure. If *check_hostname* is set, then host is matched against the + host name(s) allowed by the server cert. .. versionchanged:: 3.2 *context* parameter added. + .. versionchanged:: 3.4 + *check_hostname* and *Server Name Indicator* support (see + :data:`~ssl.HAS_SNI`) + One exception is defined as an attribute of the :mod:`poplib` module: @@ -187,14 +192,15 @@ the unique id for that message in the form ``'response mesgnum uid``, otherwise result is list ``(response, ['mesgnum uid', ...], octets)``. -.. method:: POP3.stls(context=None) +.. method:: POP3.stls(context=None, check_hostname=False) Start a TLS session on the active connection as specified in :rfc:`2595`. This is only allowed before user authentication *context* parameter is a :class:`ssl.SSLContext` object which allows bundling SSL configuration options, certificates and private keys into - a single (potentially long-lived) structure. + a single (potentially long-lived) structure. If *check_hostname* is set, + then host is matched against the host name(s) allowed by the server cert. .. versionadded:: 3.4 diff -r 63c1fbc4de4b Lib/poplib.py --- a/Lib/poplib.py Mon Nov 25 23:19:58 2013 +0100 +++ b/Lib/poplib.py Tue Nov 26 00:40:45 2013 +0100 @@ -372,7 +372,7 @@ return caps - def stls(self, context=None): + def stls(self, context=None, check_hostname=False): """Start a TLS session on the active connection as specified in RFC 2595. context - a ssl.SSLContext @@ -386,8 +386,23 @@ raise error_proto('-ERR STLS not supported by server') if context is None: context = ssl._create_stdlib_context() + will_verify = context.verify_mode != ssl.CERT_NONE + if check_hostname is None: + check_hostname = will_verify + elif check_hostname and not will_verify: + raise ValueError("check_hostname needs a SSL context with " + "either CERT_OPTIONAL or CERT_REQUIRED") resp = self._shortcmd('STLS') - self.sock = context.wrap_socket(self.sock) + server_hostname = self.host if ssl.HAS_SNI else None + self.sock = context.wrap_socket(self.sock, + server_hostname=server_hostname) + if check_hostname: + try: + ssl.match_hostname(self.sock.getpeercert(), self.host) + except Exception: + self.sock.shutdown(socket.SHUT_RDWR) + self.sock.close() + raise self.file = self.sock.makefile('rb') self._tls_established = True return resp @@ -411,7 +426,8 @@ """ def __init__(self, host, port=POP3_SSL_PORT, keyfile=None, certfile=None, - timeout=socket._GLOBAL_DEFAULT_TIMEOUT, context=None): + timeout=socket._GLOBAL_DEFAULT_TIMEOUT, context=None, + check_hostname=False): if context is not None and keyfile is not None: raise ValueError("context and keyfile arguments are mutually " "exclusive") @@ -423,12 +439,28 @@ if context is None: context = ssl._create_stdlib_context(certfile=certfile, keyfile=keyfile) + will_verify = context.verify_mode != ssl.CERT_NONE + if check_hostname is None: + check_hostname = will_verify + elif check_hostname and not will_verify: + raise ValueError("check_hostname needs a SSL context with " + "either CERT_OPTIONAL or CERT_REQUIRED") self.context = context + self._check_hostname = check_hostname POP3.__init__(self, host, port, timeout) def _create_socket(self, timeout): sock = POP3._create_socket(self, timeout) - sock = self.context.wrap_socket(sock) + server_hostname = self.host if ssl.HAS_SNI else None + try: + sock = self.context.wrap_socket(sock, + server_hostname=server_hostname) + if self._check_hostname: + ssl.match_hostname(sock.getpeercert(), self.host) + except Exception: + sock.shutdown(socket.SHUT_RDWR) + sock.close() + raise return sock def stls(self, keyfile=None, certfile=None, context=None): diff -r 63c1fbc4de4b Lib/test/test_poplib.py --- a/Lib/test/test_poplib.py Mon Nov 25 23:19:58 2013 +0100 +++ b/Lib/test/test_poplib.py Tue Nov 26 00:40:45 2013 +0100 @@ -23,7 +23,10 @@ import ssl SUPPORTS_SSL = True - CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "keycert.pem") + BASEDIR = os.path.dirname(__file__) or os.curdir + CERTFILE = os.path.join(BASEDIR, "keycert3.pem") + CAFILE = os.path.join(BASEDIR, "pycacert.pem") + requires_ssl = skipUnless(SUPPORTS_SSL, 'SSL not supported') # the dummy data returned by server when LIST and RETR commands are issued