Rev 6614: (vila) Use ssl.match_hostname instead of our own. (Vincent Ladeuil) in file:///srv/pqm.bazaar-vcs.org/archives/thelove/bzr/%2Btrunk/
Patch Queue Manager
pqm at pqm.ubuntu.com
Sun Jan 31 13:36:59 UTC 2016
At file:///srv/pqm.bazaar-vcs.org/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 6614 [merge]
revision-id: pqm at pqm.ubuntu.com-20160131133659-ouy92ee2wlv9xz8m
parent: pqm at pqm.ubuntu.com-20160122083200-1mrbddrmyxim19hg
parent: v.ladeuil+lp at free.fr-20160131125531-5magd1q1njwkal3a
committer: Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Sun 2016-01-31 13:36:59 +0000
message:
(vila) Use ssl.match_hostname instead of our own. (Vincent Ladeuil)
modified:
bzrlib/errors.py errors.py-20050309040759-20512168c4e14fbd
bzrlib/tests/test_https_urllib.py test_https_urllib.py-20111220105828-v3g3fknv8inj2jqv-1
bzrlib/transport/http/_urllib2_wrappers.py _urllib2_wrappers.py-20060913231729-ha9ugi48ktx481ao-1
doc/en/release-notes/bzr-2.7.txt bzr2.7.txt-20130727124539-wnx897hy9l2h9f7x-1
=== modified file 'bzrlib/errors.py'
--- a/bzrlib/errors.py 2013-10-04 09:56:23 +0000
+++ b/bzrlib/errors.py 2016-01-27 13:36:17 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2005-2011 Canonical Ltd
+# Copyright (C) 2005-2013, 2016 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -1686,14 +1686,6 @@
TransportError.__init__(self, msg, orig_error=orig_error)
-class CertificateError(TransportError):
-
- _fmt = "Certificate error: %(error)s"
-
- def __init__(self, error):
- self.error = error
-
-
class InvalidHttpRange(InvalidHttpResponse):
_fmt = "Invalid http range %(range)r for %(path)s: %(msg)s"
=== modified file 'bzrlib/tests/test_https_urllib.py'
--- a/bzrlib/tests/test_https_urllib.py 2013-05-20 16:38:11 +0000
+++ b/bzrlib/tests/test_https_urllib.py 2016-01-31 12:55:31 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2011,2012 Canonical Ltd
+# Copyright (C) 2011, 2012, 2013, 2016 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,24 +19,21 @@
"""
import os
-import ssl
+import sys
from bzrlib import (
config,
trace,
- )
+)
from bzrlib.errors import (
- CertificateError,
ConfigOptionValueError,
- )
-from bzrlib.tests import (
- TestCase,
- TestCaseInTempDir,
- )
+)
+from bzrlib import tests
from bzrlib.transport.http import _urllib2_wrappers
-
-
-class CaCertsConfigTests(TestCaseInTempDir):
+from bzrlib.transport.http._urllib2_wrappers import ssl
+
+
+class CaCertsConfigTests(tests.TestCaseInTempDir):
def get_stack(self, content):
return config.MemoryStack(content.encode('utf-8'))
@@ -58,6 +55,7 @@
self.overrideAttr(_urllib2_wrappers.opt_ssl_ca_certs, 'default',
os.path.join(self.test_dir, u"nonexisting.pem"))
self.warnings = []
+
def warning(*args):
self.warnings.append(args[0] % args[1:])
self.overrideAttr(trace, 'warning', warning)
@@ -67,7 +65,7 @@
"is not valid for \"ssl.ca_certs\"")
-class CertReqsConfigTests(TestCaseInTempDir):
+class CertReqsConfigTests(tests.TestCaseInTempDir):
def test_default(self):
stack = config.MemoryStack("")
@@ -82,35 +80,41 @@
self.assertRaises(ConfigOptionValueError, stack.get, "ssl.cert_reqs")
-class MatchHostnameTests(TestCase):
+class MatchHostnameTests(tests.TestCase):
+
+ def setUp(self):
+ super(MatchHostnameTests, self).setUp()
+ if sys.version_info < (2, 7, 9):
+ raise tests.TestSkipped(
+ 'python version too old to provide proper'
+ ' https hostname verification')
def test_no_certificate(self):
self.assertRaises(ValueError,
- _urllib2_wrappers.match_hostname, {}, "example.com")
+ ssl.match_hostname, {}, "example.com")
def test_wildcards_in_cert(self):
def ok(cert, hostname):
- _urllib2_wrappers.match_hostname(cert, hostname)
+ ssl.match_hostname(cert, hostname)
+
+ def not_ok(cert, hostname):
+ self.assertRaises(
+ ssl.CertificateError,
+ ssl.match_hostname, cert, hostname)
# Python Issue #17980: avoid denials of service by refusing more than
# one wildcard per fragment.
- cert = {'subject': ((('commonName', 'a*b.com'),),)}
- ok(cert, 'axxb.com')
- cert = {'subject': ((('commonName', 'a*b.co*'),),)}
- ok(cert, 'axxb.com')
- cert = {'subject': ((('commonName', 'a*b*.com'),),)}
- try:
- _urllib2_wrappers.match_hostname(cert, 'axxbxxc.com')
- except ValueError as e:
- self.assertIn("too many wildcards", str(e))
+ ok({'subject': ((('commonName', 'a*b.com'),),)}, 'axxb.com')
+ not_ok({'subject': ((('commonName', 'a*b.co*'),),)}, 'axxb.com')
+ not_ok({'subject': ((('commonName', 'a*b*.com'),),)}, 'axxbxxc.com')
def test_no_valid_attributes(self):
- self.assertRaises(CertificateError, _urllib2_wrappers.match_hostname,
+ self.assertRaises(ssl.CertificateError, ssl.match_hostname,
{"Problem": "Solved"}, "example.com")
def test_common_name(self):
cert = {'subject': ((('commonName', 'example.com'),),)}
self.assertIs(None,
- _urllib2_wrappers.match_hostname(cert, "example.com"))
- self.assertRaises(CertificateError, _urllib2_wrappers.match_hostname,
+ ssl.match_hostname(cert, "example.com"))
+ self.assertRaises(ssl.CertificateError, ssl.match_hostname,
cert, "example.org")
=== modified file 'bzrlib/transport/http/_urllib2_wrappers.py'
--- a/bzrlib/transport/http/_urllib2_wrappers.py 2013-05-20 16:38:11 +0000
+++ b/bzrlib/transport/http/_urllib2_wrappers.py 2016-01-31 12:55:31 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006-2012 Canonical Ltd
+# Copyright (C) 2006-2013, 2016 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -56,6 +56,7 @@
import urllib2
import urlparse
import re
+import ssl
import sys
import time
@@ -70,23 +71,33 @@
transport,
ui,
urlutils,
- )
-lazy_import.lazy_import(globals(), """
-import ssl
-""")
+)
+
+try:
+ _ = (ssl.match_hostname, ssl.CertificateError)
+except AttributeError:
+ # Provide fallbacks for python < 2.7.9
+ def match_hostname(cert, host):
+ trace.warning(
+ '%s cannot be verified, https certificates verification is only'
+ ' available for python versions >= 2.7.9' % (host,))
+ ssl.match_hostname = match_hostname
+ ssl.CertificateError = ValueError
# Note for packagers: if there is no package providing certs for your platform,
# the curl project produces http://curl.haxx.se/ca/cacert.pem weekly.
_ssl_ca_certs_known_locations = [
- u'/etc/ssl/certs/ca-certificates.crt', # Ubuntu/debian/gentoo
- u'/etc/pki/tls/certs/ca-bundle.crt', # Fedora/CentOS/RH
- u'/etc/ssl/ca-bundle.pem', # OpenSuse
- u'/etc/ssl/cert.pem', # OpenSuse
- u"/usr/local/share/certs/ca-root-nss.crt", # FreeBSD
+ u'/etc/ssl/certs/ca-certificates.crt', # Ubuntu/debian/gentoo
+ u'/etc/pki/tls/certs/ca-bundle.crt', # Fedora/CentOS/RH
+ u'/etc/ssl/ca-bundle.pem', # OpenSuse
+ u'/etc/ssl/cert.pem', # OpenSuse
+ u"/usr/local/share/certs/ca-root-nss.crt", # FreeBSD
# XXX: Needs checking, can't trust the interweb ;) -- vila 2012-01-25
- u'/etc/openssl/certs/ca-certificates.crt', # Solaris
- ]
+ u'/etc/openssl/certs/ca-certificates.crt', # Solaris
+]
+
+
def default_ca_certs():
if sys.platform == 'win32':
return os.path.join(os.path.dirname(sys.executable), u"cacert.pem")
@@ -115,13 +126,12 @@
def cert_reqs_from_store(unicode_str):
import ssl
try:
- return {
- "required": ssl.CERT_REQUIRED,
- "none": ssl.CERT_NONE
- }[unicode_str]
+ return {"required": ssl.CERT_REQUIRED,
+ "none": ssl.CERT_NONE}[unicode_str]
except KeyError:
raise ValueError("invalid value %s" % unicode_str)
+
def default_ca_reqs():
if sys.platform in ('win32', 'darwin'):
# FIXME: Once we get a native access to root certificates there, this
@@ -131,10 +141,10 @@
return u'required'
opt_ssl_ca_certs = config.Option('ssl.ca_certs',
- from_unicode=ca_certs_from_store,
- default=default_ca_certs,
- invalid='warning',
- help="""\
+ from_unicode=ca_certs_from_store,
+ default=default_ca_certs,
+ invalid='warning',
+ help="""\
Path to certification authority certificates to trust.
This should be a valid path to a bundle containing all root Certificate
@@ -144,10 +154,10 @@
""")
opt_ssl_cert_reqs = config.Option('ssl.cert_reqs',
- default=default_ca_reqs,
- from_unicode=cert_reqs_from_store,
- invalid='error',
- help="""\
+ default=default_ca_reqs,
+ from_unicode=cert_reqs_from_store,
+ invalid='error',
+ help="""\
Whether to require a certificate from the remote side. (default:required)
Possible values:
@@ -398,68 +408,6 @@
self._wrap_socket_for_reporting(self.sock)
-# These two methods were imported from Python 3.2's ssl module
-
-def _dnsname_to_pat(dn, max_wildcards=1):
- pats = []
- for frag in dn.split(r'.'):
- if frag.count('*') > max_wildcards:
- # Python Issue #17980: avoid denials of service by refusing more
- # than one wildcard per fragment. A survery of established
- # policy among SSL implementations showed it to be a
- # reasonable choice.
- raise ValueError(
- "too many wildcards in certificate DNS name: " + repr(dn))
- if frag == '*':
- # When '*' is a fragment by itself, it matches a non-empty dotless
- # fragment.
- pats.append('[^.]+')
- else:
- # Otherwise, '*' matches any dotless fragment.
- frag = re.escape(frag)
- pats.append(frag.replace(r'\*', '[^.]*'))
- return re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
-
-
-def match_hostname(cert, hostname):
- """Verify that *cert* (in decoded format as returned by
- SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 rules
- are mostly followed, but IP addresses are not accepted for *hostname*.
-
- CertificateError is raised on failure. On success, the function
- returns nothing.
- """
- if not cert:
- raise ValueError("empty or no certificate")
- dnsnames = []
- san = cert.get('subjectAltName', ())
- for key, value in san:
- if key == 'DNS':
- if _dnsname_to_pat(value).match(hostname):
- return
- dnsnames.append(value)
- if not san:
- # The subject is only checked when subjectAltName is empty
- for sub in cert.get('subject', ()):
- for key, value in sub:
- # XXX according to RFC 2818, the most specific Common Name
- # must be used.
- if key == 'commonName':
- if _dnsname_to_pat(value).match(hostname):
- return
- dnsnames.append(value)
- if len(dnsnames) > 1:
- raise errors.CertificateError(
- "hostname %r doesn't match either of %s"
- % (hostname, ', '.join(map(repr, dnsnames))))
- elif len(dnsnames) == 1:
- raise errors.CertificateError("hostname %r doesn't match %r" %
- (hostname, dnsnames[0]))
- else:
- raise errors.CertificateError("no appropriate commonName or "
- "subjectAltName fields were found")
-
-
class HTTPSConnection(AbstractHTTPConnection, httplib.HTTPSConnection):
def __init__(self, host, port=None, key_file=None, cert_file=None,
@@ -503,9 +451,10 @@
"'bzr help ssl.ca_certs' for more information on setting "
"trusted CAs.")
try:
- ssl_sock = ssl.wrap_socket(self.sock, self.key_file, self.cert_file,
+ ssl_sock = ssl.wrap_socket(
+ self.sock, self.key_file, self.cert_file,
cert_reqs=cert_reqs, ca_certs=ca_certs)
- except ssl.SSLError, e:
+ except ssl.SSLError:
trace.note(
"\n"
"See `bzr help ssl.ca_certs` for how to specify trusted CA"
@@ -515,7 +464,7 @@
raise
if cert_reqs == ssl.CERT_REQUIRED:
peer_cert = ssl_sock.getpeercert()
- match_hostname(peer_cert, host)
+ ssl.match_hostname(peer_cert, host)
# Wrap the ssl socket before anybody use it
self._wrap_socket_for_reporting(ssl_sock)
@@ -833,7 +782,7 @@
% (request, request.connection.sock.getsockname())
response = connection.getresponse()
convert_to_addinfourl = True
- except (ssl.SSLError, errors.CertificateError):
+ except (ssl.SSLError, ssl.CertificateError):
# Something is wrong with either the certificate or the hostname,
# re-trying won't help
raise
=== modified file 'doc/en/release-notes/bzr-2.7.txt'
--- a/doc/en/release-notes/bzr-2.7.txt 2016-01-22 08:02:12 +0000
+++ b/doc/en/release-notes/bzr-2.7.txt 2016-01-28 13:13:28 +0000
@@ -70,6 +70,9 @@
or UnicodeEncodeError when given unicode strings rather than bytes.
(Vincent Ladeuil, #106898)
+* Use ssl.match_hostname from the python ssl module and stop carrying a
+ specific version that has become obsolete. (Vincent Ladeuil, #1538480)
+
Changed Behaviour
*****************
More information about the bazaar-commits
mailing list