classification
Title: New behavior of OpenSSL hostname verification not exposed, incorrectly documented
Type: behavior Stage: patch review
Components: Documentation, Extension Modules, Library (Lib), SSL Versions: Python 3.8, Python 3.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: docs@python Nosy List: christian.heimes, docs@python, josh.r, miss-islington, ned.deily
Priority: normal Keywords: 3.6regression, patch

Created on 2019-05-09 18:15 by josh.r, last changed 2019-06-03 19:02 by miss-islington.

Pull Requests
URL Status Linked Edit
PR 13248 merged christian.heimes, 2019-05-11 15:46
PR 13784 merged miss-islington, 2019-06-03 18:51
Messages (4)
msg341990 - (view) Author: Josh Rosenberg (josh.r) * (Python triager) Date: 2019-05-09 18:15
The What's New in 3.7 docs mention the change from #31399 to use OpenSSL's built-in hostname verification ( https://docs.python.org/3/whatsnew/3.7.html#ssl ), but aside from that, information about the change is largely undiscoverable and/or wrong.

Specific problems:

1. The What's New docs repeatedly mention SSLContext.host_flags as the means of modifying behavior. The actual property is underscore prefixed though, as SSLContext._host_flags. Since SSLContext supports the creation of arbitrary names via __dict__, assigning to context.host_flags silently "works", it just fails to *do* anything (nothing ever reads it).

2. None of the flags are documented anywhere; the only way to discover them is to import _ssl (they're not exposed on ssl itself, just the internal C extension), then scan through the exposed names (they're all prefixed with HOSTFLAG_ AFAICT)

3. All of the flags are raw numeric values, but it seems like they should be IntEnums, like the other flags exposed by SSL (among other things, it would make it much easier to interpret the default _host_flags (currently it's just 4, when it could display as <HostFlags.HOSTFLAG_NO_PARTIAL_WILDCARDS: 4>)

4. Nothing about this change, _host_flags/host_flags, or the values of the flags themselves is mentioned on the ssl docs at all.

5. This unintentionally made a behavioral change (one that bit me, and may bite other folks using docker swarm, NETBIOS hostnames, etc.). Python's match_hostname implementation was fine with host names containing underscores (e.g. if the cert was wildcarded to *.example.com, it would match a_b_c.example.com just fine); they're not technically legal by the strict reading of the specs for host names (they're apparently legal for domain names, but not host names, which differ in ways I don't fully understand), but stuff like docker swarm names their services that way automatically, most (all?) browsers support visiting them, etc. It looks like OpenSSL (at least the 1.1.0g my Python 3.7.2 was built against) treats underscores as unmatchable, so any attempt to connect to such a host name in Python 3.7.2 dies with a SSLCertVerificationError, claiming a "Hostname mismatch, certificate is not valid for 'name_with_underscores.example.com'."

I discovered all this because 3.7 broke some scripts I use to connect to docker swarm services. Before I realized the issue was underscores, I was trying to figure out how to tweak the host name checks (assuming maybe something was broken with wildcard matching), and stumbled across all the other issues with the docs, the lack of flag definition exposure, etc.

For the record, I think it's reasonable to require legal host names (it was easy enough to fix for my case; I just updated our docker DNS server to provide aliases using only hyphens and changed the script to use the alias host names), but it would be nice if it was explicitly documented, and ideally, that Python itself recognize that underscores won't work and explicitly raise an exception saying why, rather than letting OpenSSL perform the rejection with a (to someone who doesn't know about the underscore issue) confusing error message.
msg342202 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2019-05-11 16:03
The entry in whatsnew is a documentation bug. Initially I wanted to expose host_flags and wrote the whatnew entry for it. Later we decided against the flag and an only implemented the hostname_checks_common_name switch (https://docs.python.org/3/library/ssl.html#ssl.SSLContext.hostname_checks_common_name).

1) SSLContext.host_flags in whatsnew is a bug. I'm updating the text.

2/3/4) The _host_flags attribute and the HOSTFLAG_* attributes are for internal use only to provide the hostname_checks_common_name flag.

5) Underscore is not a valid character for hostnames in A, AAAA, CNAME, and similar DNS record types. It's used in e.g. SRV record types, but an application will never directly connect to a SRV record address. It looks like OpenSSL interprets RFC 6125 (https://tools.ietf.org/html/rfc6125#section-6.4.3) strictly and requires valid DNS names.

I wonder, how did you get your DNS server to accept underscores? In theory you should run into a DNS exception earlier.
msg344460 - (view) Author: miss-islington (miss-islington) Date: 2019-06-03 18:51
New changeset 47eb2234061524562a4b484e3a395f4fdd6c1b76 by Miss Islington (bot) (Christian Heimes) in branch 'master':
bpo-36868: Fix what's new for SSLContext.hostname_checks_common_name (GH-13248)
https://github.com/python/cpython/commit/47eb2234061524562a4b484e3a395f4fdd6c1b76
msg344465 - (view) Author: miss-islington (miss-islington) Date: 2019-06-03 19:02
New changeset cad4ff65eb12649cd650059b15d8e12f2ae951ef by Miss Islington (bot) in branch '3.7':
bpo-36868: Fix what's new for SSLContext.hostname_checks_common_name (GH-13248)
https://github.com/python/cpython/commit/cad4ff65eb12649cd650059b15d8e12f2ae951ef
History
Date User Action Args
2019-06-03 19:02:14miss-islingtonsetmessages: + msg344465
2019-06-03 18:51:40miss-islingtonsetkeywords: + patch
stage: patch review
pull_requests: + pull_request13669
2019-06-03 18:51:30miss-islingtonsetnosy: + miss-islington
messages: + msg344460
2019-05-11 16:03:07christian.heimessetkeywords: - patch

messages: + msg342202
stage: patch review -> (no value)
2019-05-11 15:46:31christian.heimessetkeywords: + patch
stage: patch review
pull_requests: + pull_request13159
2019-05-09 22:25:53vstinnersetnosy: - vstinner
2019-05-09 18:40:00ned.deilysetnosy: + ned.deily
2019-05-09 18:15:47josh.rcreate