Hello community,
here is the log from the commit of package python-backports.ssl_match_hostname
for openSUSE:Factory checked in at 2019-03-08 11:59:12
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-backports.ssl_match_hostname (Old)
and
/work/SRC/openSUSE:Factory/.python-backports.ssl_match_hostname.new.28833 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-backports.ssl_match_hostname"
Fri Mar 8 11:59:12 2019 rev:8 rq:681234 version:3.7.0.1
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-backports.ssl_match_hostname/python-backports.ssl_match_hostname.changes
2018-09-04 22:57:39.973307341 +0200
+++
/work/SRC/openSUSE:Factory/.python-backports.ssl_match_hostname.new.28833/python-backports.ssl_match_hostname.changes
2019-03-08 11:59:15.099976739 +0100
@@ -1,0 +2,14 @@
+Sun Mar 3 11:59:42 UTC 2019 - John Vandenberg <[email protected]>
+
+- Activate new test added in v3.7.0.1
+- Enable builds on all Python 3, as Python 3.6 now benefits from
+ this backport, and it is valid to use it on Python 3.7 also.
+- Update to v3.7.0.1
+ * Support universal wheels
+ * Move the README and LICENSE from the package directory to the toplevel
+ * Update the code to what is inside of Python-3.7
+ * Add helper for determining when upstream changes
+ * Fix some problems found by unittesting
+ * Add backport of python-3.7's unittests
+
+-------------------------------------------------------------------
Old:
----
backports.ssl_match_hostname-3.5.0.1.tar.gz
New:
----
backports.ssl_match_hostname-3.7.0.1.tar.gz
test_ssl.py
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-backports.ssl_match_hostname.spec ++++++
--- /var/tmp/diff_new_pack.B7lw9u/_old 2019-03-08 11:59:16.267976541 +0100
+++ /var/tmp/diff_new_pack.B7lw9u/_new 2019-03-08 11:59:16.267976541 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-backports.ssl_match_hostname
#
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -12,18 +12,15 @@
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
-# Please submit bugfixes or comments via http://bugs.opensuse.org/
+# Please submit bugfixes or comments via https://bugs.opensuse.org/
#
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
-%if %{python3_version_nodots} >= 35
-%define skip_python3 1
-%endif
Name: python-backports.ssl_match_hostname
-Version: 3.5.0.1
+Version: 3.7.0.1
Release: 0
-Summary: The ssl.match_hostname() function from Python 3.5
+Summary: The ssl.match_hostname() function from Python 3.7
License: Python-2.0
Group: Development/Languages/Python
URL: http://bitbucket.org/brandon/backports.ssl_match_hostname
@@ -35,8 +32,10 @@
# https://pypi.python.org/pypi/backports/
# https://www.python.org/dev/peps/pep-0420/%23namespace-packages-today
# If you need to link, the python-backports package is built as a subpackage
of python-configparser
+Source1:
https://bitbucket.org/brandon/backports.ssl_match_hostname/raw/312648aec6f01702e8704a99b54445ffc2458033/tests/test_ssl.py
BuildRequires: %{python_module backports}
BuildRequires: %{python_module setuptools}
+BuildRequires: %{python_module unittest2}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
Requires: python-backports
@@ -73,6 +72,7 @@
%prep
%setup -q -n backports.ssl_match_hostname-%{version}
+cp %{SOURCE1} .
%build
%python_build
@@ -83,9 +83,12 @@
%python_expand rm -rf %{buildroot}%{$python_sitelib}/backports/__pycache__/
%python_expand %fdupes
%{buildroot}%{$python_sitelib}/backports/ssl_match_hostname/
+%check
+%python_exec -m unittest2 test_ssl.py
+
%files %{python_files}
-%doc backports/ssl_match_hostname/README.txt
-%license backports/ssl_match_hostname/LICENSE.txt
+%doc README.txt
+%license LICENSE.txt
%{python_sitelib}/backports/ssl_match_hostname/
%{python_sitelib}/backports.ssl_match_hostname-%{version}-py*.egg-info
++++++ backports.ssl_match_hostname-3.5.0.1.tar.gz ->
backports.ssl_match_hostname-3.7.0.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/backports.ssl_match_hostname-3.5.0.1/LICENSE.txt
new/backports.ssl_match_hostname-3.7.0.1/LICENSE.txt
--- old/backports.ssl_match_hostname-3.5.0.1/LICENSE.txt 1970-01-01
01:00:00.000000000 +0100
+++ new/backports.ssl_match_hostname-3.7.0.1/LICENSE.txt 2018-03-22
20:02:53.000000000 +0100
@@ -0,0 +1,51 @@
+Python License (Python-2.0)
+
+Python License, Version 2 (Python-2.0)
+
+PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
+--------------------------------------------
+
+1. This LICENSE AGREEMENT is between the Python Software Foundation
+("PSF"), and the Individual or Organization ("Licensee") accessing and
+otherwise using this software ("Python") in source or binary form and
+its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, PSF
+hereby grants Licensee a nonexclusive, royalty-free, world-wide
+license to reproduce, analyze, test, perform and/or display publicly,
+prepare derivative works, distribute, and otherwise use Python
+alone or in any derivative version, provided, however, that PSF's
+License Agreement and PSF's notice of copyright, i.e., "Copyright (c)
+2001-2013 Python Software Foundation; All Rights Reserved" are retained in
+Python alone or in any derivative version prepared by Licensee.
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python.
+
+4. PSF is making Python available to Licensee on an "AS IS"
+basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. Nothing in this License Agreement shall be deemed to create any
+relationship of agency, partnership, or joint venture between PSF and
+Licensee. This License Agreement does not grant permission to use PSF
+trademarks or trade name in a trademark sense to endorse or promote
+products or services of Licensee, or any third party.
+
+8. By copying, installing or otherwise using Python, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/backports.ssl_match_hostname-3.5.0.1/PKG-INFO
new/backports.ssl_match_hostname-3.7.0.1/PKG-INFO
--- old/backports.ssl_match_hostname-3.5.0.1/PKG-INFO 2015-12-19
23:33:16.000000000 +0100
+++ new/backports.ssl_match_hostname-3.7.0.1/PKG-INFO 2019-01-12
23:25:54.000000000 +0100
@@ -1,13 +1,13 @@
Metadata-Version: 1.1
Name: backports.ssl_match_hostname
-Version: 3.5.0.1
+Version: 3.7.0.1
Summary: The ssl.match_hostname() function from Python 3.5
Home-page: http://bitbucket.org/brandon/backports.ssl_match_hostname
Author: Toshio Kuratomi
Author-email: [email protected]
License: Python Software Foundation License
Description:
- The ssl.match_hostname() function from Python 3.5
+ The ssl.match_hostname() function from Python 3.7
=================================================
The Secure Sockets Layer is only actually *secure*
@@ -38,25 +38,17 @@
...
Brandon Craig Rhodes is merely the packager of this distribution;
- the actual code inside comes from Python 3.5 with small changes for
+ the actual code inside comes from Python 3.7 with small changes for
portability.
Requirements
------------
- * If you want to verify hosts match with certificates via ServerAltname
- IPAddress fields, you need to install the `ipaddress module`_.
- backports.ssl_match_hostname will continue to work without ipaddress
but
- will only be able to handle ServerAltName DNSName fields, not
IPAddress.
- System packagers (Linux distributions, et al) are encouraged to add
- this as a hard dependency in their packages.
-
* If you need to use this on Python versions earlier than 2.6 you will
need to
install the `ssl module`_. From Python 2.6 upwards ``ssl`` is
included in
the Python Standard Library so you do not need to install it
separately.
- .. _`ipaddress module`:: https://pypi.python.org/pypi/ipaddress
.. _`ssl module`:: https://pypi.python.org/pypi/ssl
History
@@ -71,11 +63,13 @@
* It was updated in python-3.5 to handle IPAddresses in ServerAltName
fields
(something that backports.ssl_match_hostname will do if you also
install the
ipaddress library from pypi).
+ * It was updated in python-3.7 to handle IPAddresses without the
ipaddress library and dropped
+ support for partial wildcards
+ .. _`ipaddress module`:: https://pypi.python.org/pypi/ipaddress
.. _RFC2818: http://tools.ietf.org/html/rfc2818.html
-
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: License :: OSI Approved :: Python Software Foundation License
@@ -86,4 +80,10 @@
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.0
Classifier: Programming Language :: Python :: 3.1
+Classifier: Programming Language :: Python :: 3.2
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
Classifier: Topic :: Security :: Cryptography
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/backports.ssl_match_hostname-3.5.0.1/README.txt
new/backports.ssl_match_hostname-3.7.0.1/README.txt
--- old/backports.ssl_match_hostname-3.5.0.1/README.txt 1970-01-01
01:00:00.000000000 +0100
+++ new/backports.ssl_match_hostname-3.7.0.1/README.txt 2018-03-22
20:06:08.000000000 +0100
@@ -0,0 +1,63 @@
+
+The ssl.match_hostname() function from Python 3.7
+=================================================
+
+The Secure Sockets Layer is only actually *secure*
+if you check the hostname in the certificate returned
+by the server to which you are connecting,
+and verify that it matches to hostname
+that you are trying to reach.
+
+But the matching logic, defined in `RFC2818`_,
+can be a bit tricky to implement on your own.
+So the ``ssl`` package in the Standard Library of Python 3.2
+and greater now includes a ``match_hostname()`` function
+for performing this check instead of requiring every application
+to implement the check separately.
+
+This backport brings ``match_hostname()`` to users
+of earlier versions of Python.
+Simply make this distribution a dependency of your package,
+and then use it like this::
+
+ from backports.ssl_match_hostname import match_hostname, CertificateError
+ [...]
+ sslsock = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_SSLv23,
+ cert_reqs=ssl.CERT_REQUIRED, ca_certs=...)
+ try:
+ match_hostname(sslsock.getpeercert(), hostname)
+ except CertificateError, ce:
+ ...
+
+Brandon Craig Rhodes is merely the packager of this distribution;
+the actual code inside comes from Python 3.7 with small changes for
+portability.
+
+
+Requirements
+------------
+
+* If you need to use this on Python versions earlier than 2.6 you will need to
+ install the `ssl module`_. From Python 2.6 upwards ``ssl`` is included in
+ the Python Standard Library so you do not need to install it separately.
+
+.. _`ssl module`:: https://pypi.python.org/pypi/ssl
+
+History
+-------
+
+* This function was introduced in python-3.2
+* It was updated for python-3.4a1 for a CVE
+ (backports-ssl_match_hostname-3.4.0.1)
+* It was updated from RFC2818 to RFC 6125 compliance in order to fix another
+ security flaw for python-3.3.3 and python-3.4a5
+ (backports-ssl_match_hostname-3.4.0.2)
+* It was updated in python-3.5 to handle IPAddresses in ServerAltName fields
+ (something that backports.ssl_match_hostname will do if you also install the
+ ipaddress library from pypi).
+* It was updated in python-3.7 to handle IPAddresses without the ipaddress
library and dropped
+ support for partial wildcards
+
+.. _`ipaddress module`:: https://pypi.python.org/pypi/ipaddress
+
+.. _RFC2818: http://tools.ietf.org/html/rfc2818.html
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/backports.ssl_match_hostname-3.5.0.1/backports/ssl_match_hostname/LICENSE.txt
new/backports.ssl_match_hostname-3.7.0.1/backports/ssl_match_hostname/LICENSE.txt
---
old/backports.ssl_match_hostname-3.5.0.1/backports/ssl_match_hostname/LICENSE.txt
2015-12-19 23:30:28.000000000 +0100
+++
new/backports.ssl_match_hostname-3.7.0.1/backports/ssl_match_hostname/LICENSE.txt
1970-01-01 01:00:00.000000000 +0100
@@ -1,51 +0,0 @@
-Python License (Python-2.0)
-
-Python License, Version 2 (Python-2.0)
-
-PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
---------------------------------------------
-
-1. This LICENSE AGREEMENT is between the Python Software Foundation
-("PSF"), and the Individual or Organization ("Licensee") accessing and
-otherwise using this software ("Python") in source or binary form and
-its associated documentation.
-
-2. Subject to the terms and conditions of this License Agreement, PSF
-hereby grants Licensee a nonexclusive, royalty-free, world-wide
-license to reproduce, analyze, test, perform and/or display publicly,
-prepare derivative works, distribute, and otherwise use Python
-alone or in any derivative version, provided, however, that PSF's
-License Agreement and PSF's notice of copyright, i.e., "Copyright (c)
-2001-2013 Python Software Foundation; All Rights Reserved" are retained in
-Python alone or in any derivative version prepared by Licensee.
-
-3. In the event Licensee prepares a derivative work that is based on
-or incorporates Python or any part thereof, and wants to make
-the derivative work available to others as provided herein, then
-Licensee hereby agrees to include in any such work a brief summary of
-the changes made to Python.
-
-4. PSF is making Python available to Licensee on an "AS IS"
-basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
-DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
-INFRINGE ANY THIRD PARTY RIGHTS.
-
-5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
-FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
-A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
-OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-6. This License Agreement will automatically terminate upon a material
-breach of its terms and conditions.
-
-7. Nothing in this License Agreement shall be deemed to create any
-relationship of agency, partnership, or joint venture between PSF and
-Licensee. This License Agreement does not grant permission to use PSF
-trademarks or trade name in a trademark sense to endorse or promote
-products or services of Licensee, or any third party.
-
-8. By copying, installing or otherwise using Python, Licensee
-agrees to be bound by the terms and conditions of this License
-Agreement.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/backports.ssl_match_hostname-3.5.0.1/backports/ssl_match_hostname/README.txt
new/backports.ssl_match_hostname-3.7.0.1/backports/ssl_match_hostname/README.txt
---
old/backports.ssl_match_hostname-3.5.0.1/backports/ssl_match_hostname/README.txt
2015-12-19 23:30:28.000000000 +0100
+++
new/backports.ssl_match_hostname-3.7.0.1/backports/ssl_match_hostname/README.txt
1970-01-01 01:00:00.000000000 +0100
@@ -1,69 +0,0 @@
-
-The ssl.match_hostname() function from Python 3.5
-=================================================
-
-The Secure Sockets Layer is only actually *secure*
-if you check the hostname in the certificate returned
-by the server to which you are connecting,
-and verify that it matches to hostname
-that you are trying to reach.
-
-But the matching logic, defined in `RFC2818`_,
-can be a bit tricky to implement on your own.
-So the ``ssl`` package in the Standard Library of Python 3.2
-and greater now includes a ``match_hostname()`` function
-for performing this check instead of requiring every application
-to implement the check separately.
-
-This backport brings ``match_hostname()`` to users
-of earlier versions of Python.
-Simply make this distribution a dependency of your package,
-and then use it like this::
-
- from backports.ssl_match_hostname import match_hostname, CertificateError
- [...]
- sslsock = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_SSLv23,
- cert_reqs=ssl.CERT_REQUIRED, ca_certs=...)
- try:
- match_hostname(sslsock.getpeercert(), hostname)
- except CertificateError, ce:
- ...
-
-Brandon Craig Rhodes is merely the packager of this distribution;
-the actual code inside comes from Python 3.5 with small changes for
-portability.
-
-
-Requirements
-------------
-
-* If you want to verify hosts match with certificates via ServerAltname
- IPAddress fields, you need to install the `ipaddress module`_.
- backports.ssl_match_hostname will continue to work without ipaddress but
- will only be able to handle ServerAltName DNSName fields, not IPAddress.
- System packagers (Linux distributions, et al) are encouraged to add
- this as a hard dependency in their packages.
-
-* If you need to use this on Python versions earlier than 2.6 you will need to
- install the `ssl module`_. From Python 2.6 upwards ``ssl`` is included in
- the Python Standard Library so you do not need to install it separately.
-
-.. _`ipaddress module`:: https://pypi.python.org/pypi/ipaddress
-.. _`ssl module`:: https://pypi.python.org/pypi/ssl
-
-History
--------
-
-* This function was introduced in python-3.2
-* It was updated for python-3.4a1 for a CVE
- (backports-ssl_match_hostname-3.4.0.1)
-* It was updated from RFC2818 to RFC 6125 compliance in order to fix another
- security flaw for python-3.3.3 and python-3.4a5
- (backports-ssl_match_hostname-3.4.0.2)
-* It was updated in python-3.5 to handle IPAddresses in ServerAltName fields
- (something that backports.ssl_match_hostname will do if you also install the
- ipaddress library from pypi).
-
-
-.. _RFC2818: http://tools.ietf.org/html/rfc2818.html
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/backports.ssl_match_hostname-3.5.0.1/backports/ssl_match_hostname/__init__.py
new/backports.ssl_match_hostname-3.7.0.1/backports/ssl_match_hostname/__init__.py
---
old/backports.ssl_match_hostname-3.5.0.1/backports/ssl_match_hostname/__init__.py
2015-12-19 23:30:28.000000000 +0100
+++
new/backports.ssl_match_hostname-3.7.0.1/backports/ssl_match_hostname/__init__.py
2018-03-22 20:10:11.000000000 +0100
@@ -1,82 +1,134 @@
-"""The match_hostname() function from Python 3.3.3, essential when using
SSL."""
+"""The match_hostname() function from Python 3.7.0, essential when using
SSL."""
-import re
import sys
+import socket as _socket
-# ipaddress has been backported to 2.6+ in pypi. If it is installed on the
-# system, use it to handle IPAddress ServerAltnames (this was added in
-# python-3.5) otherwise only do DNS matching. This allows
-# backports.ssl_match_hostname to continue to be used all the way back to
-# python-2.4.
try:
- import ipaddress
-except ImportError:
- ipaddress = None
+ # Divergence: Python-3.7+'s _ssl has this exception type but older Pythons
do not
+ from _ssl import SSLCertVerificationError
+ CertificateError = SSLCertVerificationError
+except:
+ class CertificateError(ValueError):
+ pass
-__version__ = '3.5.0.1'
+__version__ = '3.7.0.1'
-class CertificateError(ValueError):
- pass
+# Divergence: Added to deal with ipaddess as bytes on python2
+def _to_text(obj):
+ if isinstance(obj, str) and sys.version_info < (3,):
+ obj = unicode(obj, encoding='ascii', errors='strict')
+ elif sys.version_info >= (3,) and isinstance(obj, bytes):
+ obj = str(obj, encoding='ascii', errors='strict')
+ return obj
-def _dnsname_match(dn, hostname, max_wildcards=1):
+
+def _to_bytes(obj):
+ if isinstance(obj, str) and sys.version_info >= (3,):
+ obj = bytes(obj, encoding='ascii', errors='strict')
+ elif sys.version_info < (3,) and isinstance(obj, unicode):
+ obj = obj.encode('ascii', 'strict')
+ return obj
+
+
+def _dnsname_match(dn, hostname):
"""Matching according to RFC 6125, section 6.4.3
- http://tools.ietf.org/html/rfc6125#section-6.4.3
+ - Hostnames are compared lower case.
+ - For IDNA, both dn and hostname must be encoded as IDN A-label (ACE).
+ - Partial wildcards like 'www*.example.org', multiple wildcards, sole
+ wildcard or wildcards in labels other then the left-most label are not
+ supported and a CertificateError is raised.
+ - A wildcard must match at least one character.
"""
- pats = []
if not dn:
return False
- # Ported from python3-syntax:
- # leftmost, *remainder = dn.split(r'.')
- parts = dn.split(r'.')
- leftmost = parts[0]
- remainder = parts[1:]
-
- wildcards = leftmost.count('*')
- if wildcards > max_wildcards:
- # Issue #17980: avoid denials of service by refusing more
- # than one wildcard per fragment. A survey of established
- # policy among SSL implementations showed it to be a
- # reasonable choice.
- raise CertificateError(
- "too many wildcards in certificate DNS name: " + repr(dn))
-
+ wildcards = dn.count('*')
# speed up common case w/o wildcards
if not wildcards:
return dn.lower() == hostname.lower()
- # RFC 6125, section 6.4.3, subitem 1.
- # The client SHOULD NOT attempt to match a presented identifier in which
- # the wildcard character comprises a label other than the left-most label.
- if leftmost == '*':
- # When '*' is a fragment by itself, it matches a non-empty dotless
- # fragment.
- pats.append('[^.]+')
- elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
- # RFC 6125, section 6.4.3, subitem 3.
- # The client SHOULD NOT attempt to match a presented identifier
- # where the wildcard character is embedded within an A-label or
- # U-label of an internationalized domain name.
- pats.append(re.escape(leftmost))
+ if wildcards > 1:
+ # Divergence .format() to percent formatting for Python < 2.6
+ raise CertificateError(
+ "too many wildcards in certificate DNS name: %s" % repr(dn))
+
+ dn_leftmost, sep, dn_remainder = dn.partition('.')
+
+ if '*' in dn_remainder:
+ # Only match wildcard in leftmost segment.
+ # Divergence .format() to percent formatting for Python < 2.6
+ raise CertificateError(
+ "wildcard can only be present in the leftmost label: "
+ "%s." % repr(dn))
+
+ if not sep:
+ # no right side
+ # Divergence .format() to percent formatting for Python < 2.6
+ raise CertificateError(
+ "sole wildcard without additional labels are not support: "
+ "%s." % repr(dn))
+
+ if dn_leftmost != '*':
+ # no partial wildcard matching
+ # Divergence .format() to percent formatting for Python < 2.6
+ raise CertificateError(
+ "partial wildcards in leftmost label are not supported: "
+ "%s." % repr(dn))
+
+ hostname_leftmost, sep, hostname_remainder = hostname.partition('.')
+ if not hostname_leftmost or not sep:
+ # wildcard must match at least one char
+ return False
+ return dn_remainder.lower() == hostname_remainder.lower()
+
+
+def _inet_paton(ipname):
+ """Try to convert an IP address to packed binary form
+
+ Supports IPv4 addresses on all platforms and IPv6 on platforms with IPv6
+ support.
+ """
+ # inet_aton() also accepts strings like '1'
+ # Divergence: We make sure we have native string type for all python
versions
+ try:
+ b_ipname = _to_bytes(ipname)
+ except UnicodeError:
+ raise ValueError("%s must be an all-ascii string." % repr(ipname))
+
+ # Set ipname in native string format
+ if sys.version_info < (3,):
+ n_ipname = b_ipname
else:
- # Otherwise, '*' matches any dotless string, e.g. www*
- pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
+ n_ipname = ipname
- # add the remaining fragments, ignore any wildcards
- for frag in remainder:
- pats.append(re.escape(frag))
+ if n_ipname.count('.') == 3:
+ try:
+ return _socket.inet_aton(n_ipname)
+ # Divergence: OSError on late python3. socket.error earlier.
+ # Null bytes generate ValueError on python3(we want to raise
+ # ValueError anyway), TypeError # earlier
+ except (OSError, _socket.error, TypeError):
+ pass
- pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
- return pat.match(hostname)
+ try:
+ return _socket.inet_pton(_socket.AF_INET6, n_ipname)
+ # Divergence: OSError on late python3. socket.error earlier.
+ # Null bytes generate ValueError on python3(we want to raise
+ # ValueError anyway), TypeError # earlier
+ except (OSError, _socket.error, TypeError):
+ # Divergence .format() to percent formatting for Python < 2.6
+ raise ValueError("%s is neither an IPv4 nor an IP6 "
+ "address." % repr(ipname))
+ except AttributeError:
+ # AF_INET6 not available
+ pass
+ # Divergence .format() to percent formatting for Python < 2.6
+ raise ValueError("%s is not an IPv4 address." % repr(ipname))
-def _to_unicode(obj):
- if isinstance(obj, str) and sys.version_info < (3,):
- obj = unicode(obj, encoding='ascii', errors='strict')
- return obj
def _ipaddress_match(ipname, host_ip):
"""Exact matching of IP addresses.
@@ -85,15 +137,19 @@
(section 1.7.2 - "Out of Scope").
"""
# OpenSSL may add a trailing newline to a subjectAltName's IP address
- # Divergence from upstream: ipaddress can't handle byte str
- ip = ipaddress.ip_address(_to_unicode(ipname).rstrip())
+ ip = _inet_paton(ipname.rstrip())
return ip == host_ip
def match_hostname(cert, hostname):
"""Verify that *cert* (in decoded format as returned by
SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125
- rules are followed, but IP addresses are not accepted for *hostname*.
+ rules are followed.
+
+ The function matches IP addresses rather than dNSNames if hostname is a
+ valid ipaddress string. IPv4 addresses are supported on all platforms.
+ IPv6 addresses are supported on platforms with IPv6 support (AF_INET6
+ and inet_pton).
CertificateError is raised on failure. On success, the function
returns nothing.
@@ -103,22 +159,16 @@
"SSL socket or SSL context with either "
"CERT_OPTIONAL or CERT_REQUIRED")
try:
- # Divergence from upstream: ipaddress can't handle byte str
- host_ip = ipaddress.ip_address(_to_unicode(hostname))
+ # Divergence: Deal with hostname as bytes
+ host_ip = _inet_paton(_to_text(hostname))
except ValueError:
# Not an IP address (common case)
host_ip = None
except UnicodeError:
- # Divergence from upstream: Have to deal with ipaddress not taking
- # byte strings. addresses should be all ascii, so we consider it not
- # an ipaddress in this case
+ # Divergence: Deal with hostname as byte strings.
+ # IP addresses should be all ascii, so we consider it not
+ # an IP address if this fails
host_ip = None
- except AttributeError:
- # Divergence from upstream: Make ipaddress library optional
- if ipaddress is None:
- host_ip = None
- else:
- raise
dnsnames = []
san = cert.get('subjectAltName', ())
for key, value in san:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/backports.ssl_match_hostname-3.5.0.1/setup.cfg
new/backports.ssl_match_hostname-3.7.0.1/setup.cfg
--- old/backports.ssl_match_hostname-3.5.0.1/setup.cfg 1970-01-01
01:00:00.000000000 +0100
+++ new/backports.ssl_match_hostname-3.7.0.1/setup.cfg 2018-03-22
20:02:53.000000000 +0100
@@ -0,0 +1,2 @@
+[bdist_wheel]
+universal=1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/backports.ssl_match_hostname-3.5.0.1/setup.py
new/backports.ssl_match_hostname-3.7.0.1/setup.py
--- old/backports.ssl_match_hostname-3.5.0.1/setup.py 2015-12-19
23:30:28.000000000 +0100
+++ new/backports.ssl_match_hostname-3.7.0.1/setup.py 2018-03-22
20:06:08.000000000 +0100
@@ -4,12 +4,12 @@
from distutils.core import setup
long_description = open(os.path.join(
- os.path.dirname(__file__), 'backports', 'ssl_match_hostname', 'README.txt',
+ os.path.dirname(__file__), 'README.txt'
)).read()
setup(
name='backports.ssl_match_hostname',
- version='3.5.0.1',
+ version='3.7.0.1',
description='The ssl.match_hostname() function from Python 3.5',
long_description=long_description,
author='Brandon Rhodes',
@@ -28,6 +28,12 @@
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.0',
'Programming Language :: Python :: 3.1',
+ 'Programming Language :: Python :: 3.2',
+ 'Programming Language :: Python :: 3.3',
+ 'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: 3.7',
'Topic :: Security :: Cryptography',
],
packages=['backports', 'backports.ssl_match_hostname'],
++++++ test_ssl.py ++++++
# coding: utf-8
# Test the support for SSL and sockets
import sys
import socket
# Divergence: unittest2 so that we have assertRaisesRegexp
if sys.version_info < (3,):
import unittest2 as unittest
else:
import unittest
from backports import ssl_match_hostname as ssl
class BasicSocketTests(unittest.TestCase):
def test_match_hostname(self):
def ok(cert, hostname):
ssl.match_hostname(cert, hostname)
def fail(cert, hostname):
self.assertRaises(ssl.CertificateError,
ssl.match_hostname, cert, hostname)
# -- Hostname matching --
cert = {'subject': ((('commonName', 'example.com'),),)}
ok(cert, 'example.com')
ok(cert, 'ExAmple.cOm')
fail(cert, 'www.example.com')
fail(cert, '.example.com')
fail(cert, 'example.org')
fail(cert, 'exampleXcom')
cert = {'subject': ((('commonName', '*.a.com'),),)}
ok(cert, 'foo.a.com')
fail(cert, 'bar.foo.a.com')
fail(cert, 'a.com')
fail(cert, 'Xa.com')
fail(cert, '.a.com')
# only match wildcards when they are the only thing
# in left-most segment
cert = {'subject': ((('commonName', 'f*.com'),),)}
fail(cert, 'foo.com')
fail(cert, 'f.com')
fail(cert, 'bar.com')
fail(cert, 'foo.a.com')
fail(cert, 'bar.foo.com')
# NULL bytes are bad, CVE-2013-4073
cert = {'subject': ((('commonName',
'null.python.org\x00example.org'),),)}
ok(cert, 'null.python.org\x00example.org') # or raise an error?
fail(cert, 'example.org')
fail(cert, 'null.python.org')
# error cases with wildcards
cert = {'subject': ((('commonName', '*.*.a.com'),),)}
fail(cert, 'bar.foo.a.com')
fail(cert, 'a.com')
fail(cert, 'Xa.com')
fail(cert, '.a.com')
cert = {'subject': ((('commonName', 'a.*.com'),),)}
fail(cert, 'a.foo.com')
fail(cert, 'a..com')
fail(cert, 'a.com')
# wildcard doesn't match IDNA prefix 'xn--'
# Divergence: explicitly mark text string with u
idna = u'püthon.python.org'.encode("idna").decode("ascii")
cert = {'subject': ((('commonName', idna),),)}
ok(cert, idna)
cert = {'subject': ((('commonName', 'x*.python.org'),),)}
fail(cert, idna)
cert = {'subject': ((('commonName', 'xn--p*.python.org'),),)}
fail(cert, idna)
# wildcard in first fragment and IDNA A-labels in sequent fragments
# are supported.
# Divergence: explicitly mark text strings with u
idna = u'www*.pythön.org'.encode("idna").decode("ascii")
cert = {'subject': ((('commonName', idna),),)}
fail(cert, u'www.pythön.org'.encode("idna").decode("ascii"))
fail(cert, u'www1.pythön.org'.encode("idna").decode("ascii"))
fail(cert, u'ftp.pythön.org'.encode("idna").decode("ascii"))
fail(cert, u'pythön.org'.encode("idna").decode("ascii"))
# Slightly fake real-world example
cert = {'notAfter': 'Jun 26 21:41:46 2011 GMT',
'subject': ((('commonName', 'linuxfrz.org'),),),
'subjectAltName': (('DNS', 'linuxfr.org'),
('DNS', 'linuxfr.com'),
('othername', '<unsupported>'))}
ok(cert, 'linuxfr.org')
ok(cert, 'linuxfr.com')
# Not a "DNS" entry
fail(cert, '<unsupported>')
# When there is a subjectAltName, commonName isn't used
fail(cert, 'linuxfrz.org')
# A pristine real-world example
cert = {'notAfter': 'Dec 18 23:59:59 2011 GMT',
'subject': ((('countryName', 'US'),),
(('stateOrProvinceName', 'California'),),
(('localityName', 'Mountain View'),),
(('organizationName', 'Google Inc'),),
(('commonName', 'mail.google.com'),))}
ok(cert, 'mail.google.com')
fail(cert, 'gmail.com')
# Only commonName is considered
fail(cert, 'California')
# -- IPv4 matching --
cert = {'subject': ((('commonName', 'example.com'),),),
'subjectAltName': (('DNS', 'example.com'),
('IP Address', '10.11.12.13'),
('IP Address', '14.15.16.17'))}
ok(cert, '10.11.12.13')
ok(cert, '14.15.16.17')
fail(cert, '14.15.16.18')
fail(cert, 'example.net')
# -- IPv6 matching --
if hasattr(socket, 'AF_INET6'):
cert = {'subject': ((('commonName', 'example.com'),),),
'subjectAltName': (
('DNS', 'example.com'),
('IP Address', '2001:0:0:0:0:0:0:CAFE\n'),
('IP Address', '2003:0:0:0:0:0:0:BABA\n'))}
ok(cert, '2001::cafe')
ok(cert, '2003::baba')
fail(cert, '2003::bebe')
fail(cert, 'example.net')
# -- Miscellaneous --
# Neither commonName nor subjectAltName
cert = {'notAfter': 'Dec 18 23:59:59 2011 GMT',
'subject': ((('countryName', 'US'),),
(('stateOrProvinceName', 'California'),),
(('localityName', 'Mountain View'),),
(('organizationName', 'Google Inc'),))}
fail(cert, 'mail.google.com')
# No DNS entry in subjectAltName but a commonName
cert = {'notAfter': 'Dec 18 23:59:59 2099 GMT',
'subject': ((('countryName', 'US'),),
(('stateOrProvinceName', 'California'),),
(('localityName', 'Mountain View'),),
(('commonName', 'mail.google.com'),)),
'subjectAltName': (('othername', 'blabla'), )}
ok(cert, 'mail.google.com')
# No DNS entry subjectAltName and no commonName
cert = {'notAfter': 'Dec 18 23:59:59 2099 GMT',
'subject': ((('countryName', 'US'),),
(('stateOrProvinceName', 'California'),),
(('localityName', 'Mountain View'),),
(('organizationName', 'Google Inc'),)),
'subjectAltName': (('othername', 'blabla'),)}
fail(cert, 'google.com')
# Empty cert / no cert
self.assertRaises(ValueError, ssl.match_hostname, None, 'example.com')
self.assertRaises(ValueError, ssl.match_hostname, {}, 'example.com')
# Issue #17980: avoid denials of service by refusing more than one
# wildcard per fragment.
cert = {'subject': ((('commonName', 'a*b.example.com'),),)}
with self.assertRaisesRegex(
ssl.CertificateError,
"partial wildcards in leftmost label are not supported"):
ssl.match_hostname(cert, 'axxb.example.com')
cert = {'subject': ((('commonName', 'www.*.example.com'),),)}
with self.assertRaisesRegex(
ssl.CertificateError,
"wildcard can only be present in the leftmost label"):
ssl.match_hostname(cert, 'www.sub.example.com')
cert = {'subject': ((('commonName', 'a*b*.example.com'),),)}
with self.assertRaisesRegex(
ssl.CertificateError,
"too many wildcards"):
ssl.match_hostname(cert, 'axxbxxc.example.com')
cert = {'subject': ((('commonName', '*'),),)}
with self.assertRaisesRegex(
ssl.CertificateError,
"sole wildcard without additional labels are not support"):
ssl.match_hostname(cert, 'host')
cert = {'subject': ((('commonName', '*.com'),),)}
with self.assertRaisesRegex(
ssl.CertificateError,
r"hostname 'com' doesn't match '\*.com'"):
ssl.match_hostname(cert, 'com')
# extra checks for _inet_paton()
for invalid in ['1', '', '1.2.3', '256.0.0.1', '127.0.0.1/24']:
with self.assertRaises(ValueError):
ssl._inet_paton(invalid)
for ipaddr in ['127.0.0.1', '192.168.0.1']:
self.assertTrue(ssl._inet_paton(ipaddr))
if hasattr(socket, 'AF_INET6'):
for ipaddr in ['::1', '2001:db8:85a3::8a2e:370:7334']:
self.assertTrue(ssl._inet_paton(ipaddr))